Railway Operation Simulator  v2.7.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
63  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
64  int IncrementalDigitsIn, int SignallerMaxSpeedIn): RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
65  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
66  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
67  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
68 /*
69  Construct a new train with general default values and input values for position and headcode.
70  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
71  This is because trains are kept in a vector and vectors erase elements during internal operations.
72  Deletion is explicit by using a special function. Increment the static class member NextTrainID
73  after setting this train's ID.
74 */
75 
76 {
77  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
78  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
79  AnsiString(TrainModeIn));
80  // AutoControl = true;//all trains start in auto control
81  UpdateCounter = 0;
82  TimeTimeLocArrived = false;
83  Derailed = false;
84  DerailPending = false;
85  Crashed = false;
86  StoppedAtBuffers = false;
87  StoppedAtSignal = false;
88  StoppedAtLocation = false;
89  StoppedAfterSPAD = false;
90  StoppedWithoutPower = false; // new at v2.4.0
91  StoppedForTrainInFront = false;
92  SignallerStoppingFlag = false;
93  SignallerStopped = false;
94  SignallerRemoved = false;
95  NotInService = false;
96  HoldAtLocationInTTMode = false;
97  AllowedToPassRedSignal = false;
98  CallingOnFlag = false;
99  BeingCalledOn = false;
100  DepartureTimeSet = false;
102  TimetableFinished = false;
103  LastActionDelayFlag = false;
104  OneLengthAccelDecel = false;
105  TrainCrashedInto = -1;
107  Plotted = false;
108  TrainGone = false;
109  SPADFlag = false;
110  FrontCodePtr = new Graphics::TBitmap;
111  FrontCodePtr->PixelFormat = pf8bit;
112  FrontCodePtr->Height = 8;
113  FrontCodePtr->Width = 8;
115  FrontCodePtr->Transparent = false;
116  AValue = sqrt(2 * PowerAtRail / Mass);
118  TerminatedMessageSent = false;
119  JoinedOtherTrainFlag = false;
121  StepForwardFlag = false;
123  for(int x = 0; x < 4; x++)
124  {
125  HeadCodeGrPtr[x] = new Graphics::TBitmap;
126  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
127  HeadCodeGrPtr[x]->Height = 8;
128  HeadCodeGrPtr[x]->Width = 8;
130  HeadCodeGrPtr[x]->Transparent = false;
131  }
132  for(int x = 0; x < 4; x++)
133  {
134  BackgroundPtr[x] = new Graphics::TBitmap;
135  BackgroundPtr[x]->PixelFormat = pf8bit;
136  BackgroundPtr[x]->Height = 8;
137  BackgroundPtr[x]->Width = 8;
139  BackgroundPtr[x]->Transparent = false;
140  }
141  for(int x = 0; x < 4; x++)
142  {
144  // set here to ensure have values
145  }
146  for(int x = 0; x < 4; x++)
147  {
148  PlotElement[x] = -1; // marker for not plotted yet
149  }
150  for(int x = 0; x < 3; x++)
151  {
152  OldZoomOutElement[x] = -1; // marker for not plotted yet
153  }
155  NextTrainID++;
156 
157  // new values added to complete initialisation of all TTrain variables
158 
159  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
160  // TrainDataEntryPtr, initialise in AddTrain
162  FrontElementLength = 0;
163  EntrySpeed = 0;
164  ExitSpeedHalf = 0;
165  ExitSpeedFull = 0;
166  MaxExitSpeed = 0;
167  BrakeRate = 0;
169  FirstHalfMove = true;
170  EntryTime = 0;
171  ExitTimeHalf = 0;
172  ExitTimeFull = 0;
173  ReleaseTime = 0;
174  TRSTime = 0;
175  LastActionTime = 0;
176  Straddle = MidLag;
177  LeadElement = -1;
178  LeadEntryPos = 0;
179  LeadExitPos = 0;
180  MidElement = -1;
181  MidEntryPos = 0;
182  MidExitPos = 0;
183  LagElement = -1;
184  LagEntryPos = 0;
185  LagExitPos = 0;
186  TrainFailed = false; // added at v2.4.0
187  for(int x = 0; x < 4; x++)
188  {
189  HOffset[x] = 0;
190  VOffset[x] = 0;
191  PlotEntryPos[x] = 0;
192  }
193  OpTimeToAct = 60; // default value, new at v2.2.0
194  MinsDelayed = 0.0; // new at v2.2.0
195  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
196  FinishJoinLogSent = false;
197  // added at v2.4.0 to prevent repeatdly logging the event
200  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
204  ZeroPowerNoCDTMessage = false;
209  TrainFailurePending = false;
210  Utilities->CallLogPop(648);
211 }
212 
213 // ---------------------------------------------------------------------------
214 
215 void TTrain::DeleteTrain(int Caller)
216 /*
217  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
218  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
219  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
220  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
221  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
222  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
223  No need to delete HeadCodePosition as that just points to existing bitmaps
224 */ {
225  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
226  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
227  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
228  if(Display->ZoomOutFlag)
230  if(FrontCodePtr == 0)
231  {
232  throw Exception("Error in attempting to delete FrontCodePtr");
233  }
234  delete FrontCodePtr;
235  FrontCodePtr = 0;
236  for(int x = 0; x < 4; x++)
237  {
238  if(BackgroundPtr[x] == 0)
239  {
240  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
241  }
242  delete BackgroundPtr[x];
243  BackgroundPtr[x] = 0;
244  }
245  for(int x = 0; x < 4; x++)
246  {
247  if(HeadCodeGrPtr[x] == 0)
248  {
249  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
250  }
251  delete HeadCodeGrPtr[x];
252  HeadCodeGrPtr[x] = 0;
253  }
254  Utilities->CallLogPop(649);
255 }
256 
257 // ---------------------------------------------------------------------------
258 
260 /*
261  Plots the train starting position on screen. Note that the check for starting on straight points &
262  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
263  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
264  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
265  Set the headcode graphics pointers from the headcode text, then check whether starting at a
266  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
267  for the continuation element. Otherwise set Lead and Mid values,
268 
269  and Lead element value unless
270  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
271  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
272  then check if a train on either Mid or Lag and if so give a warning message and return false so
273  that the calling function can delete the train. Plot the Mid element train values then do similarly
274  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
275  the train. Finally set the Plotted flag and return true.
276 */ {
277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
278  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
279 
281  // PlotStartTime = TrainController->TTClockTime;
282  FirstHalfMove = true;
283 
284  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
285  // 'claim' it for this train to prevent any other waiting trains trying to enter
287  {
288  LagElement = -1; // not to be plotted
289  LagExitPos = 0; // not to be plotted
290  LagEntryPos = 0; // not to be plotted
291  MidElement = -1; // not to be plotted
292  MidExitPos = 0; // not to be plotted
293  MidEntryPos = 0; // not to be plotted
295  LeadExitPos = 1; // will be 1 for continuation entry
296  LeadEntryPos = 0;
297 
299  MaxExitSpeed = StartSpeed; // initial value
301  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
302  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
303  if(EntrySpeed > SpeedLimit)
304  EntrySpeed = SpeedLimit;
308  // LeadElement is the element to be entered
309 
310  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
311  // can achieve ExitSpeedFull at the half braking rate.
313  {
314  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
315  if(TempEntrySpeed < EntrySpeed)
316  {
317  EntrySpeed = TempEntrySpeed;
319  }
320  }
321  Straddle = MidLag; // only for starting on a continuation
323  // no need to stop gap flashing if start on continuation
324  }
325  else // not starting at a continuation
326  {
327  LagElement = -1;
328  LagEntryPos = 0;
329  LagExitPos = 0;
336 
338  MaxExitSpeed = StartSpeed; // initial value
340  bool TempDerail = false; // dummy
341  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
343  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
344  {
345  StoppedWithoutPower = true;
346  }
347  // facing buffers check - ignore starting speed if start facing buffers
348  StoppedAtBuffers = false;
349  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
352  {
353  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
354  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
355  EntrySpeed = 0;
356  ExitSpeedHalf = 0;
357  ExitSpeedFull = 0;
358  MaxExitSpeed = 0;
359  // SetTrainMovementValues not called so set this here
360  BrakeRate = 0;
363  StoppedAtSignal = false;
364  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
365  // signal check is an 'else'
366  if(!StoppedAtLocation)
367  StoppedAtBuffers = true; // stopped at location takes precedence
368  }
369 
370  // facing continuation check - don't allow to stop even if no power
372  {
373  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
374  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
378  BrakeRate = 0;
379  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
380  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
381  }
382 
383  // Signal check
384  else if((NextElementPosition > -1) && (NextEntryPos > -1))
385  // condition check added as precaution after SloughIECC error reported by James U
386  {
387  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
388  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
389  {
390  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
391  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
392  EntrySpeed = 0;
393  ExitSpeedHalf = 0;
394  ExitSpeedFull = 0;
395  MaxExitSpeed = 0;
396  BrakeRate = 0;
399  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
400  {
401  StoppedAtSignal = true;
403  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
404  }
406  { // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
407  // forwards, but don't change the background colour so still shows as stopped at location
408  StoppedAtSignal = true;
409  }
410  }
411  else
412  {
413  StoppedAtSignal = false;
414  if(NextEntryPos > 1)
415  {
416  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
417  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
418  }
419  else
420  {
421  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
422  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
423  }
424  if(EntrySpeed > SpeedLimit)
425  EntrySpeed = SpeedLimit;
429  TDateTime TestTime = TrainController->TTClockTime; // test
430  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
431  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
432  // NextElement is the element to be entered
433 
434  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
435  // can achieve ExitSpeedFull at the half braking rate.
437  {
438  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
439  // half braking
440  if(TempEntrySpeed < EntrySpeed)
441  {
442  EntrySpeed = TempEntrySpeed;
443  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
444  }
445  }
446  }
447  }
449  {
450  throw Exception("Error, LeadElement Exit Connection is NotSet");
451  }
452  }
453 
454  if(MidElement > -1) // will be -1 if start on continuation
455  {
456  Straddle = LeadMid;
460  {
461  for(int x = 0; x < 4; x++)
462  {
463  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
464  }
465  }
466  else
467  {
468  for(int x = 0; x < 4; x++)
469  {
471  }
472  }
473  if(TrainMode == Timetable)
475  else
478  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
479 
482 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
483  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
484  {
485  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
486  Utilities->CallLogPop(651);
487  return false;
488  }
489 */
494  PlotTrainGraphic(8, 0, Display);
495  PlotTrainGraphic(9, 1, Display);
496 
499 
500  // pick up background bitmaps [2] & [3]
501 
504 
505  PlotElement[2] = MidElement;
507  PlotElement[3] = MidElement;
509  PlotTrainGraphic(10, 2, Display);
510  PlotTrainGraphic(11, 3, Display);
511  // Plotted = true; set in PlotTrainGraphic
512  }
513  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
514  Utilities->CallLogPop(652);
515 }
516 
517 // ---------------------------------------------------------------------------
518 void TTrain::UnplotTrain(int Caller)
519 {
520  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
521  if(!Plotted)
522  return;
523 
524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
525 
526  if(Straddle == MidLag)
527  {
528  if(MidElement > -1)
529  {
534  // to force plot of locked route marker, needed once only for the element
535  }
536  if(LagElement > -1)
537  {
542  // to force plot of locked route marker, needed once only for the element
543  }
544  }
545  else if(Straddle == LeadMidLag)
546  {
547  if(LeadElement > -1)
548  {
551  // to force plot of locked route marker, needed once only for the element
552  }
553  if(MidElement > -1)
554  {
559  // to force plot of locked route marker, needed once only for the element
560  }
561  if(LagElement > -1)
562  {
565  // to force plot of locked route marker, needed once only for the element
566  }
567  }
568  else if(Straddle == LeadMid)
569  {
570  if(LeadElement > -1)
571  {
576  // to force plot of locked route marker, needed once only for the element
577  }
578  if(MidElement > -1)
579  {
584  // to force plot of locked route marker, needed once only for the element
585  }
586  }
587 
588  if(LeadElement > -1)
590  if(MidElement > -1)
592  if(LagElement > -1)
594  Plotted = false;
596  Display->Update();
597  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
598  // resurrected when Update() dropped from PlotOutput etc
599  Utilities->CallLogPop(653);
600 }
601 
602 // ----------------------------------------------------------------------------
603 
604 void TTrain::UpdateTrain(int Caller)
605 /*
606  Note: Some changes made since comments written
607 
608  Brief:
609  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
610  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
611  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
612  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
613  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
614  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
615  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
616  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
617  changed to MidLag within the function and all elements moved down one, old Mid becomes
618  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
619  incremented to reflect the position the train now occupies.
620 
621  Detail:
622  Set TrainFailurePending if all conditions met
623  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
624  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
625  and return.
626  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
627  If there's a LagElement (there will be but include check for good practice - next
628  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
629  train fully on offending point - Derail set and DerailPanding reset, train background
630  colour changed (note that BackgroundColour is a property of the train itself) then return.
631  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
632  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
633  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
634  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
635  if LeadElement is a fouled trailing point.
636  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
637  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
638  replotting the last background segment and checking whether the element is a bridge or crossover with the other
639  track in a route, in which case the route colour is replotted.
640  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
641  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
642  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
643  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
644  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
645  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
646  train can be deleted by the calling function, and the function returns.
647  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
648  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
649  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
650  basic red aspect.
651 
652  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
653  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
654  regardless of direction, and with the correct front code colour.
655 
656  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
657  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
658  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
659  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
660  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
661  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
662  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
663 
664  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
665  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
666  changed similarly. The function then returns.
667 
668  If Crashed is not set then Straddle is incremented and the function returns.
669 */
670 
671 {
672  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
673  UpdateCounter++;
674  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
675  if(UpdateCounter >= 100)
676  UpdateCounter = 0;
677 
678  int RandRange = (TrainController->MTBFHours * 3600) / 53;
679 
680  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
681  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
682  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
683  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
684  // RandomFailureCounter value is fixed for a full cycle of train updates so this
685  // makes sure there's no bunching of failures as there is for a fixed comparison number
686  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
687  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
688  {
689  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
690  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
691  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
692  // don't fail if:
693  // (a) on a continuation (entering or leaving);
694  // (b) already failed;
695  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
696  // (d) train terminated;
697  // (e) crashed or derailed; or
698  // (f) under signaller control and stopped.
699  {
700  if((random(RandRange)) == 0)
701  // max value for RandRange is over 2x10^9
702  { // here if failure due
703  TrainFailurePending = true;
704  // fail when PlotElements set to proper Lead, Mid & Elements
705  }
706  }
707  }
708 
709 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
710  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
711  {
712  StoppedWithoutPower = true;
713  }
714 */
715  int LockedVectorNumber;
716  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
717  // default values - these needed for route checker below
718  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
719 
722 
723  if(Crashed || Derailed)
724  {
726  {
727  PlotTrain(7, Display);
728  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
729  Display->Update();
730  }
731  OpTimeToAct = 0.0;
732  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
733  Utilities->CallLogPop(1017);
734  return; // no further action, user has to remove or work around
735  }
736 
738  {
740  }
741 
743  {
745  }
746 
748  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
749  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
750  // to move & then stop again at the same station
751  {
752  TimeTimeLocArrived = false;
753  }
754 
755  if(!Stopped() && !SPADFlag && !TrainFailed)
756  {
758  }
759 
760  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
761  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
762 /* old version where force a stop at buffers regardless of speed
763  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
764  else StoppedAtBuffers = false;
765 */
766 
767  // new version where crash if run into buffers
768  if(!Crashed)
769  {
770  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
771  {
772  if(ExitSpeedFull > 1)
773  {
774  Crashed = true;
778  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
779  // no need for missed action logs - will be sent when train removed
780  StoppedAtBuffers = false;
781  }
783  // stopped at location & stopped without power take precedence
784  {
785  StoppedAtBuffers = true;
786  }
787  else
788  StoppedAtBuffers = false;
789  }
790  else
791  StoppedAtBuffers = false;
792  }
793  else
794  StoppedAtBuffers = false;
795  // if crashed don't want stopped at buffers set
796 
797  // also crash if run into a level crossing that is changing or has barriers up
798  if(!Crashed)
799  {
800  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
801  {
802  int H = Track->TrackElementAt(873, LeadElement).HLoc;
803  int V = Track->TrackElementAt(874, LeadElement).VLoc;
804  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
805  {
806  Crashed = true;
810  // no need for missed action logs - will be sent when train removed
811  }
812  }
813  }
814 
816  {
818  }
819 
820  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
821  if((TrainMode == Timetable) && StoppedAtLocation && (ActionVectorEntryPtr->Command != "")) //if Command == "" then either TimeLoc or TimeTimeLoc
822  HoldAtLocationInTTMode = true;
823  else if(TrainMode == Timetable)
824  HoldAtLocationInTTMode = false;
825  // in Signaller mode HoldAtLocationInTTMode not changed
826 
827  // check if departure pending & set times unless already set
828  if(TrainMode == Timetable)
829  {
831  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
832  {
833  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
834  {
836  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
837  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
838  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
839  DepartureTimeSet = true;
840  }
841  }
842  }
843 
845  {
846  OpTimeToAct = CalcTimeToAct(0); // called after ReleaseTime set
847  // calculate every 1 sec (in real time, not timetable time) for all trains
848  }
849 
850  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
851  if(TrainMode == Timetable)
852  {
853  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
854  {
855  RemainHereLogNotSent = true;
856  }
858  {
859  // ignore TimeLoc & TTLoc departures
860  // Action logs given in functions
862  LastActionTime + TDateTime(30.0 / 86400)))
863  {
864  if(ActionVectorEntryPtr->Command == "fsp")
865  { // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
866  FrontTrainSplit(0);
867  if(TrainFailurePending) // ok, stopped so PlotElements set
868  {
869  TrainHasFailed(0);
870  }
871  Utilities->CallLogPop(2041);
872  return;
873  }
874  else if(ActionVectorEntryPtr->Command == "rsp")
875  { // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
876  RearTrainSplit(0);
877  if(TrainFailurePending) // ok, stopped so PlotElements set
878  {
879  TrainHasFailed(1);
880  }
881  Utilities->CallLogPop(2042);
882  return;
883  }
884  else if(ActionVectorEntryPtr->Command == "Fjo")
885  FinishJoin(0);
886  else if(ActionVectorEntryPtr->Command == "jbo")
887  JoinedBy(0);
888  else if(ActionVectorEntryPtr->Command == "cdt")
890  else if(ActionVectorEntryPtr->Command == "Fns")
891  NewTrainService(0);
892  else if(ActionVectorEntryPtr->Command == "Frh")
893  RemainHere(0);
894  else if(ActionVectorEntryPtr->Command == "Fer")
895  TimetableFinished = true;
896  // other aspects of 'Fer' dealt with in TTrain::Update()
897  else if(ActionVectorEntryPtr->Command == "F-nshs")
899  else if(ActionVectorEntryPtr->Command == "Frh-sh")
901  else if(ActionVectorEntryPtr->Command == "Fns-sh")
903 /*
904  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
905  shuttle headcode (no train creation)
906  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
907  remain here
908  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
909  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
910 */
911  }
912  }
913  else
914  {
916  {
918  }
919  }
920  }
921  if(TrainMode == Timetable)
922  {
923  if(StoppedAtBuffers)
924  {
925  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
926  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
927  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
928  if(BufferLocation == "")
930  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
931  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
932  {
936  {
938  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
939  // Drop missed actions so user can still use sig mode to get back on track
941  }
942  if(TrainFailurePending) // ok, stopped so PlotElements set
943  {
945  TrainHasFailed(2);
946  }
947  Utilities->CallLogPop(1020);
948  return;
949  }
950  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !StoppedAtLocation && (TrainController->TTClockTime >
951  ReleaseTime))
952  {
955  {
958  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
959  // Drop missed actions so user can still use sig mode to get back on track
961  }
962  if(TrainFailurePending) // ok, stopped so PlotElements set
963  {
965  TrainHasFailed(3);
966  }
967  Utilities->CallLogPop(1397);
968  return;
969  }
970  }
971  else
972  {
974  }
975  }
976  else
977  {
979  }
980 
981  if(TrainMode == Timetable)
982  {
984  {
986  }
988  {
990  }
991  }
992 
993  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
994  // restart after stopped for train in front
995  int NextElementPosition, NextEntryPos;
996 
997  if(LeadElement > -1) // if an exit continuation then not set
998  {
999  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1000  {
1002  }
1003  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1004  {
1005  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1006  {
1007  LeadExitPos = 1;
1008  }
1009  else
1010  {
1011  LeadExitPos = 3;
1012  }
1013  }
1014  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1015  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1016  }
1017  else
1018  {
1019  NextElementPosition = -1;
1020  NextEntryPos = -1;
1021  }
1022 
1023  if((NextElementPosition > -1) && (NextEntryPos > -1))
1024  // may be buffers or continuation so need this check
1025  {
1026 /*
1027  Check whether calling-on conditions met:-
1028  a) approaching train has stopped at a signal but not at a location;
1029  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1030  change of direction (cdt), remaining here (Frh), or under signaller control);
1031  c) at least 1 platform available for the approaching train;
1032  d) points (if any) set for direct route into platform;
1033  e) approaching train is to stop at station;
1034  f) no more facing signals between train and platform;
1035  g) [dropped g]
1036  h) train in front preventing route being set far enough to release stop signal;
1037  i) train in front not exiting at continuation;
1038  j) signal must be within 4km of the stop platform;
1039  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily]; and
1040  l) no existing route conflicts with the route into the platform.
1041  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1042  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1043 */
1044  if(TrainMode == Timetable)
1045  {
1046  if(CallingOnAllowed(0))
1047  {
1048  CallingOnFlag = true;
1049  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1050  }
1051  else
1052  {
1053  if(CallingOnFlag)
1054  {
1056  }
1057  CallingOnFlag = false;
1058  }
1059  }
1060  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !StoppedAtLocation)
1061  {
1062  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1063  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1064  // sets StoppedAtSignal again & train doesn't move
1065  StoppedAtSignal = false;
1066  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1067  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1068  // LeadMidLag and front of train was on LeadElement (after the current move)
1070  EntrySpeed = 0;
1072  FirstHalfMove = true;
1073  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1074  // NextElement is the element to be entered
1075  }
1077  {
1078  if(ClearToNextSignal(0))
1079  {
1080  StoppedForTrainInFront = false;
1081  BeingCalledOn = false;
1082  EntrySpeed = 0;
1084  FirstHalfMove = true;
1085  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1086  }
1087  else
1088  {
1089  if(TrainFailurePending) // ok, stopped so PlotElements set
1090  {
1091  TrainHasFailed(4);
1092  }
1093  Utilities->CallLogPop(1097);
1094  return;
1095  }
1096  }
1097  }
1098 
1099  if((Straddle == MidLag) && (LeadElement != -1))
1100  // later check only for Straddle == LeadMid, so need this check here for initial train start
1101  {
1103  }
1104 
1105 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1106  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1107  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1108  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1109  which could be when start as Snt.
1110  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1111  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1112  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1113  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1114  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1115  reached.
1116  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1117  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1118  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1119  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1120  sending a message to the performancelog.
1121 */
1122 
1123  if(TrainMode == Timetable)
1124  {
1126  {
1127  if(BeingCalledOn)
1128  {
1129  StoppedForTrainInFront = true;
1130  }
1132  {
1134  }
1136  {
1137  // value updated at every scheduled departure & arrival
1139  AnsiString StationName;
1141  {
1143  }
1145  {
1147  }
1148  else
1149  {
1150  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1151  }
1152  EntrySpeed = 0;
1156  FirstHalfMove = true;
1157  StoppedAtLocation = false;
1158 
1159  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1160  {
1161  StoppedWithoutPower = true;
1162  }
1163 
1164  if((NextElementPosition > -1) && (NextEntryPos > -1))
1165  // condition check added for SloughIECC error reported by James U
1166  {
1167  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1168  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1169  {
1170  StoppedAtSignal = true;
1171  if(!StoppedWithoutPower)
1172  // if stopped without power just keep existing background colour
1173  {
1175  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1176  }
1177  }
1178  }
1180  {
1181  TimeTimeLocArrived = false;
1182  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1183  // no warning for TimeTimeLoc departure
1184  }
1185  else
1186  {
1188  }
1189  DepartureTimeSet = false;
1190  // no need to set LastActionTime for a departure
1192  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1193  // note
1194 /*
1195  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1196  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1197  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1198  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1199  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1200  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1201  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1202 */
1204  {
1205  StoppedAtBuffers = true;
1206  }
1207  else if(!StoppedWithoutPower)
1208  // if buffers or no power, don't set values
1209  {
1211  {
1212  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1213  // NextElement is the element to be entered
1214  }
1215  else
1216  {
1218  // use LeadElement for an exit continuation
1219  }
1220  }
1221  }
1222  }
1223  }
1224 
1225  if(Straddle == LeadMidLag)
1226  {
1228  {
1229  Utilities->CallLogPop(654);
1230  return;
1231  }
1232  }
1233  else
1234  {
1236  {
1237  Utilities->CallLogPop(655);
1238  return;
1239  }
1240  }
1241 
1242  if((LeadElement > -1) && (MidElement > -1))
1243  {
1245  { // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1246  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1247  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1248  SignallerStoppingFlag = false;
1249  StepForwardFlag = false;
1250  }
1251  }
1252 
1253  if(Stopped())
1254  // this is what prevents another movement if the train is stopped
1255  {
1256  if(TrainFailurePending) // ok, stopped so PlotElements set
1257  {
1258  TrainHasFailed(5);
1259  }
1260  BrakeRate = 0;
1261  Utilities->CallLogPop(656);
1262  return;
1263  }
1264 
1265  // here when ready for next move
1266 
1267  // check for train in front & if so stop at next access (when train fully on element next to train)
1268  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1269  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1270  // variable TrainInFrontInSignallerModeFlag
1271  {
1272  if(LeadElement > -1)
1273  {
1274  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1275  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1276  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1277  // true if another train on NextEntryPos track whether bridge or not
1278  {
1279  StoppedForTrainInFront = true;
1280  }
1281  else
1282  {
1283  StoppedForTrainInFront = false;
1284  }
1285  }
1286  }
1287 
1288  if((Straddle == LeadMid) && SPADFlag)
1289  // give message + plot background when ready to move half past the signal
1290  {
1291  if(NextElementPosition > -1)
1292  {
1293  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1294  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1295  {
1296  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1298  // if goes past 2 signals then give message twice
1300  }
1301  }
1302  }
1303 
1304  if(Straddle == LeadMidLag)
1305  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1306  {
1307  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1308  if(SPADFlag)
1309  {
1310  if(ExitSpeedFull == 0)
1311  {
1312  StoppedAfterSPAD = true;
1313  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1314  }
1315  }
1316 
1318  {
1319  if(ExitSpeedFull == 0)
1320  {
1321  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1322  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1323  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1324  // is sent at the right time and once only.
1325  SignallerStopped = true;
1326  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1327  StepForwardFlag = false;
1328  SignallerStoppingFlag = false;
1329  TTrackElement TE;
1330  AnsiString Loc = "";
1331  bool LocNamed = false;
1332  if(LeadElement > -1)
1333  {
1334  TE = Track->TrackElementAt(782, LeadElement);
1335  if(TE.ActiveTrackElementName != "")
1336  {
1337  Loc = TE.ActiveTrackElementName;
1338  LocNamed = true;
1339  }
1340  else
1341  {
1342  Loc = "track element " + TE.ElementID;
1343  }
1344  }
1345  if((MidElement > -1) && !LocNamed)
1346  {
1347  TE = Track->TrackElementAt(783, MidElement);
1348  if(TE.ActiveTrackElementName != "")
1349  {
1350  Loc = TE.ActiveTrackElementName;
1351  LocNamed = true;
1352  }
1353  else if(Loc == "")
1354  {
1355  Loc = "track element " + TE.ElementID;
1356  }
1357  }
1358  if(Loc == "")
1359  {
1360  Loc = "outside railway";
1361  // must have stopped after left at a continuation (because both lead & mid == -1)
1362  }
1363  else
1364  Loc = "at " + Loc;
1365  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1366  }
1367  }
1368  if(LeadElement > -1) // if an exit continuation then not set
1369  {
1370  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1371  {
1373  }
1374  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1375  {
1376  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1377  LeadExitPos = 1;
1378  else
1379  LeadExitPos = 3;
1380  }
1381  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1382  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1383  }
1384  else
1385  {
1386  NextElementPosition = -1;
1387  NextEntryPos = -1;
1388  }
1389 
1392  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1393 
1394  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1395  {
1396  StoppedWithoutPower = true;
1397  }
1398 
1399  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1400  // may be buffers or continuation. SPADFlag added at v2.1.0
1401  // so don't override the SPAD colour & don't set StoppedAtSignal
1402  {
1403  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1404  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !StoppedAtLocation)
1405  {
1406  StoppedAtSignal = true;
1407  if(!StoppedWithoutPower)
1408  // leave background as is if no power, but set StoppedAtSignal
1409  {
1411  }
1412  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1413  }
1414  }
1415 
1416  if(!Stopped())
1417  {
1418  if((NextElementPosition > -1) && (NextEntryPos > -1))
1419  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1420  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1421  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1422  // function for fuller explanation
1423  {
1424  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1425  // NextElement is the element to be entered
1426  }
1427  // follow the continuation exits:-
1428  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1429  {
1431  // Use LeadElement for calcs if lead is a continuation
1432  }
1433  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1434  {
1436  // Use MidElement for calcs if mid is a continuation
1437  }
1438  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1439  {
1441  // Use LagElement for calcs if lag is a continuation
1442  }
1443  }
1444 
1445  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1446  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1447  // Trains may not be in a route
1448  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1449  {
1450  // NB if LeadElement == -1 then the above test returns false
1451  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1452  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1453  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1454  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1455  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1456  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1457  FirstPair.second).GetELink() == TempELink))
1458  {
1459  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1460  }
1461  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1462  SecondPair.second).GetELink() == TempELink))
1463  {
1464  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1465  }
1466  }
1467 
1468  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1469  // Trains may not be in a route
1470  {
1471  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1472  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1473  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1474  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1475  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1476  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1477  FirstPair.second).GetELink() == TempELink))
1478  {
1479  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1480  }
1481  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1482  SecondPair.second).GetELink() == TempELink))
1483  {
1484  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1485  }
1486  }
1487 
1488  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1489  // Trains may not be in a route
1490  {
1491  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1492  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1493  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1494  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1495  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1496  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1497  FirstPair.second).GetELink() == TempELink))
1498  {
1499  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1500  }
1501  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1502  SecondPair.second).GetELink() == TempELink))
1503  {
1504  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1505  }
1506 
1507  AllRoutes->CheckMapAndRoutes(8); // test
1508  }
1509 
1510  if(LagElement > -1)
1511  // not entering at a continuation so can deal with train leaving the lag element
1512  {
1514  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1515  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1516  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1517 
1518  TPrefDirElement PrefDirElement;
1519  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1520  // as this is a 16x16 graphic
1522  {
1524  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1525  }
1526 
1527  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1528  {
1529  int RouteNumber;
1530  TrainGone = true;
1531  // flag to indicate train to be deleted - outside this function
1533  {
1534  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1535  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1536  // calc distance from & inc last signal to exit
1537  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1538  int NewLastElement = 0, NewLastExitPos = 0;
1539  // need above because can't change LastElement & LastExitPos until both new values obtained
1540  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1541  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1542  LastElement).TrackType != Points))
1543  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1544  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1545  // leave CumDistance as it was in these circumstances.
1546  {
1547  if(LastExitPos < 2)
1548  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1549  else
1550  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1551  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1552  if(NewLastElement == -1)
1553  // this will catch buffers or any other connection failure
1554  {
1555  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1556  }
1557  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1558  if(NewLastExitPos == -1)
1559  {
1560  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1561  }
1562  LastElement = NewLastElement;
1563  LastExitPos = NewLastExitPos;
1564  }
1565  // if at signal add this in too
1566  if(CumDistance < 1200)
1567  {
1568  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1569  }
1570  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1571  // else use 1200m - CumDistance
1572  int FirstDistance = 0;
1573  if(CumDistance >= 1200)
1574  FirstDistance = 100;
1575  else
1576  FirstDistance = 1200 - CumDistance;
1577  if(FirstDistance < 100)
1578  FirstDistance = 100; // don't allow < 100
1579  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1580  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1581  if(ExitSpeedFull > 20.0)
1582  {
1583  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1584  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1585  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1586  // 4320.0 = 3.6 * 1200, .0 to make it a double
1587  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1588  }
1589  else
1590  {
1591  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1592  ContinuationAutoSigEntry.SecondDelay = 120.0;
1593  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1594  }
1595  ContinuationAutoSigEntry.AccessNumber = 0;
1596  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1598  {
1600  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1601  VectorIT++)
1602  {
1603  if(VectorIT->RouteNumber == RouteNumber)
1604  {
1605  // another train has passed out of same route so erase earlier entry
1606  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1607  break;
1608  }
1609  }
1610  }
1611  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1612  }
1614  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1615  Display->Update();
1616  // need to keep this since Update() not called for PlotSmallOutput as too slow
1617  Utilities->CallLogPop(659);
1618  return;
1619  }
1620  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1621  if(LeadElement > -1)
1622  {
1624  // changed to lead so reset early
1625  {
1626  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1628  // don't plot if zoomed out
1629  if(!Display->ZoomOutFlag)
1631  // covers signal resetting in same direction
1632  }
1633  }
1635  {
1636  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1637  {
1638  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1639  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1640  TPrefDirElement PrefDirElement;
1641  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1643  {
1645  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1646  }
1648  LockedVectorNumber)))
1649  {
1651  }
1652  }
1653  }
1654  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1655  {
1656  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1658  // don't plot if zoomed out
1659  if(!Display->ZoomOutFlag)
1661  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1662  }
1664  {
1665  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1666  {
1667  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1668  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1669  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1670  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1671  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1672  int RouteNumber;
1674  // already know it's an autosigsroute, this is just to get the RouteNumber
1675  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1676  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1677  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1678  int RouteNumber2;
1680  // already know it's an autosigsroute, this is just to get the RouteNumber
1681  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1682  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1683  {
1684  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1685  // this was in the 1.3.0 addition but without the condition
1686  }
1687  // end of 1.3.2 addition
1688  // end of 1.3.0.addition
1689  }
1690  TPrefDirElement PrefDirElement;
1691  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1693  {
1695  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1696  }
1697  }
1698  }
1699  }
1700 
1701  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1702  if(Straddle == LeadMid)
1703  {
1704  AllowedToPassRedSignal = false;
1705  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1706  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1707  if(DerailPending)
1708  // set during last GetLeadElement, but only act on it when train fully on offending point
1709  // i.e. next time Straddle reaches LeadMid
1710  {
1711  Derailed = true;
1712  DerailPending = false;
1716  Utilities->CallLogPop(657);
1717  return;
1718  }
1725  Straddle = MidLag;
1726  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1727  // LeadElement during this function (note that if stopped at signal then won't get this far)
1728  if(LeadElement > -1)
1729  {
1731  // i.e an exit continuation only
1732  // if don't exclude entry continuations then can't progress past it
1733  {
1734  LeadElement = -1;
1735  }
1736  else
1737  {
1738  GetLeadElement(0);
1739  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1741  if(Stopped())
1742  {
1743  if(TrainFailurePending) // ok, stopped so PlotElements set
1744  {
1745  TrainHasFailed(6);
1746  }
1747  Utilities->CallLogPop(658);
1748  return; // i.e. don't move forward one step if next element is a red signal
1749  }
1750  }
1751  }
1752  }
1753 
1754  if(LagElement > -1)
1755  {
1756  // below are the actions required at both half moves for LagElement > -1
1758 
1759  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1760  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1761  // need to do this for each half element
1762 
1763  TPrefDirElement PrefDirElement;
1764  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1765  {
1766  int RouteNumber; // holder for call below - not used
1768  {
1769  if(Utilities->clTransparent == TColor(0xFFFFFF))
1770  // change to black for a white background
1771  {
1773  // only applies for AutoSigs Route in case was locked & timed out
1774  }
1775  else
1776  // change to white for a dark background
1777  {
1779  // only applies for AutoSigs Route in case was locked & timed out
1780  }
1782  }
1783  }
1784 
1786  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1787  // or a train on the opposite track - needs to be replotted
1788  }
1789 
1790  // update all array values
1791  HOffset[3] = HOffset[2];
1792  HOffset[2] = HOffset[1];
1793  HOffset[1] = HOffset[0];
1794  VOffset[3] = VOffset[2];
1795  VOffset[2] = VOffset[1];
1796  VOffset[1] = VOffset[0];
1797  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1798 
1799  BackgroundPtr[3] = BackgroundPtr[2];
1800  BackgroundPtr[2] = BackgroundPtr[1];
1801  BackgroundPtr[1] = BackgroundPtr[0];
1802  BackgroundPtr[0] = TempPtr;
1803 
1804  // update headcode graphics depending on Lead entry value
1805  if(LeadElement > -1) // if Lead is -1 then stays as is
1806  {
1808  {
1809  for(int x = 0; x < 4; x++)
1810  {
1811  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1812  }
1813  }
1814  else
1815  {
1816  for(int x = 0; x < 4; x++)
1817  {
1819  }
1820  }
1821  }
1822 
1823  if(TrainMode == Timetable)
1824  {
1826  }
1827  else
1828  {
1830  }
1832 
1833  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1834  if(LeadElement > -1)
1835  {
1836  if(Straddle == MidLag)
1837  // just about to move half onto the new lead element
1838  {
1840  // pick up new background bitmap [0]
1842  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1843  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1844  // check if own ID for entry at continuation, else crashes into itself!
1845  {
1846  // OK if crossing on a bridge
1847  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1848  if(OtherTrainEntryPos == -1)
1849  {
1850  throw Exception("Error - OtherTrainEntryPos not set");
1851  }
1852  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1853  // LeadEntryPos for rear end crashes
1854  (LeadExitPos == OtherTrainEntryPos))
1855  // LeadExitPos for head-on crashes
1856  {
1858  Crashed = true; // only set if Straddle = MidLag
1859  CallingOnFlag = false;
1860  // in case was set, need to disable call on if call on button had been pressed
1861  }
1862  }
1863  else if(MidElement > -1) // will be -1 for continuation entries
1864  {
1865  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1866  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1867  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1868  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1869  int OtherTrainID = -1;
1870  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1871  {
1872  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1873  {
1874  TrainCrashedInto = OtherTrainID;
1875  Crashed = true; // only set if Straddle = MidLag
1876  CallingOnFlag = false;
1877  // in case was set, need to disable call on if call on button had been pressed
1878  }
1879  }
1880  }
1881  }
1882  else
1883  {
1885  // pick up new background bitmap [0]
1887  }
1888  PlotElement[0] = LeadElement;
1890  PlotTrainGraphic(12, 0, Display);
1891  }
1892 
1893  if(MidElement > -1)
1894  {
1895  PlotElement[2] = MidElement;
1897  PlotTrainGraphic(1, 2, Display);
1898  }
1899 
1900  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
1901  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
1902  if(Straddle == MidLag)
1903  {
1904  if(MidElement > -1)
1905  {
1906  PlotElement[1] = MidElement;
1908  PlotTrainGraphic(2, 1, Display);
1909  }
1910  if(LagElement > -1)
1911  {
1912  PlotElement[3] = LagElement;
1914  PlotTrainGraphic(3, 3, Display);
1915  }
1916  }
1917  else // Straddle == LeadMidLag
1918  {
1919  if(LeadElement > -1)
1920  {
1921  PlotElement[1] = LeadElement;
1923  PlotTrainGraphic(4, 1, Display);
1924  }
1925  if(MidElement > -1)
1926  {
1927  PlotElement[3] = MidElement;
1929  PlotTrainGraphic(5, 3, Display);
1930  }
1931  }
1932 
1933  if(Crashed)
1934  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
1935  {
1940  // in case was set, need to disable call on if call on button had been pressed
1947  Straddle = LeadMidLag;
1948  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
1949  Display->Update();
1950  // resurrected when Update() dropped from PlotOutput etc
1951  Utilities->CallLogPop(660);
1952  return;
1953  }
1954 
1955  // deal here with station stops & pass times after all replotting done but before Straddle changed
1956  if(TrainMode == Timetable)
1957  {
1958  if(Straddle == LeadMidLag)
1959  {
1960  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
1961  {
1962  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
1963  // to point to the location arrival entry - before a change of direction
1964  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
1965  bool StopRequired = false;
1966  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
1967  if(TTVPos > -1) // -1 if can't find it or if name is ""
1968  {
1969  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
1970  // or a station where next element contains a train or a stop signal, if so
1971  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
1972  // to test the actual track the train is on since it can't be a platform
1973  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
1974  TTrackElement NextTrackElement; // default for now
1975  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
1976  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
1977  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
1978  int NextElementEntryPos = -1;
1979  int NextElementExitPos = -1;
1980  bool TrainOnNextElement = false;
1981  bool StopSignalAtNextElement = false;
1982  if(ForwardConnection)
1983  // if no forward connection can't derive anything from it without errors
1984  {
1985  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
1986  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
1987  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
1988  // this is only for signals so no need to worry about points ambiguity
1989  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
1990  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
1991  }
1992  // logic here is: if(train@stoplinkpos1 || train@stoplinkpos2 || (forward connection && (train on next element || stop signal at next element)))
1993  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
1994  {
1995  if(TTVPos > 0)
1996  {
1998  ActionVectorEntryPtr += TTVPos;
1999  }
2000  if(StopRequired)
2001  {
2002  StoppedAtLocation = true;
2003  StoppedAtSignal = false;
2004  // may have been set earlier at line 925 so need to reset as
2005  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2006  // in zoom out mode
2007  if(!TrainFailed)
2008  {
2010  // pale green
2011  }
2014  {
2015  TimeTimeLocArrived = true;
2016  // used in case of later signaller control, when need to know
2017  // whether had arrived or not, to avoid sending the arrival
2018  // message twice, see TInterface::TimetableControl1Click
2019  }
2020  }
2021  else
2022  {
2024  }
2026  {
2028  }
2029  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2031  }
2032  }
2033  }
2034  }
2035  }
2036 
2037  if(Straddle == MidLag)
2038  {
2039  Straddle = LeadMidLag;
2040  FirstHalfMove = false;
2041  }
2042  else if(Straddle == LeadMidLag)
2043  {
2044  Straddle = LeadMid;
2045  FirstHalfMove = true;
2046  }
2047  else if(Straddle == LeadMid)
2048  {
2049  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2050  }
2051 
2052  if(TrainFailurePending) // ok, moving but PlotElements set above
2053  {
2054  TrainHasFailed(7);
2055  }
2056  Display->Update();
2057  // need to keep this since Update() not called for PlotSmallOutput as too slow
2058  Utilities->CallLogPop(661);
2059 }
2060 
2061 // ----------------------------------------------------------------------------
2062 
2063 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2064 {
2065  switch(CodeChar)
2066  {
2067  case '0':
2068  return RailGraphics->Code0;
2069 
2070  case '1':
2071  return RailGraphics->Code1;
2072 
2073  case '2':
2074  return RailGraphics->Code2;
2075 
2076  case '3':
2077  return RailGraphics->Code3;
2078 
2079  case '4':
2080  return RailGraphics->Code4;
2081 
2082  case '5':
2083  return RailGraphics->Code5;
2084 
2085  case '6':
2086  return RailGraphics->Code6;
2087 
2088  case '7':
2089  return RailGraphics->Code7;
2090 
2091  case '8':
2092  return RailGraphics->Code8;
2093 
2094  case '9':
2095  return RailGraphics->Code9;
2096 
2097  case 'A':
2098  return RailGraphics->CodeA;
2099 
2100  case 'B':
2101  return RailGraphics->CodeB;
2102 
2103  case 'C':
2104  return RailGraphics->CodeC;
2105 
2106  case 'D':
2107  return RailGraphics->CodeD;
2108 
2109  case 'E':
2110  return RailGraphics->CodeE;
2111 
2112  case 'F':
2113  return RailGraphics->CodeF;
2114 
2115  case 'G':
2116  return RailGraphics->CodeG;
2117 
2118  case 'H':
2119  return RailGraphics->CodeH;
2120 
2121  case 'I':
2122  return RailGraphics->CodeI;
2123 
2124  case 'J':
2125  return RailGraphics->CodeJ;
2126 
2127  case 'K':
2128  return RailGraphics->CodeK;
2129 
2130  case 'L':
2131  return RailGraphics->CodeL;
2132 
2133  case 'M':
2134  return RailGraphics->CodeM;
2135 
2136  case 'N':
2137  return RailGraphics->CodeN;
2138 
2139  case 'O':
2140  return RailGraphics->CodeO;
2141 
2142  case 'P':
2143  return RailGraphics->CodeP;
2144 
2145  case 'Q':
2146  return RailGraphics->CodeQ;
2147 
2148  case 'R':
2149  return RailGraphics->CodeR;
2150 
2151  case 'S':
2152  return RailGraphics->CodeS;
2153 
2154  case 'T':
2155  return RailGraphics->CodeT;
2156 
2157  case 'U':
2158  return RailGraphics->CodeU;
2159 
2160  case 'V':
2161  return RailGraphics->CodeV;
2162 
2163  case 'W':
2164  return RailGraphics->CodeW;
2165 
2166  case 'X':
2167  return RailGraphics->CodeX;
2168 
2169  case 'Y':
2170  return RailGraphics->CodeY;
2171 
2172  case 'Z':
2173  return RailGraphics->CodeZ;
2174 
2175  case 'a':
2176  return RailGraphics->Code_a;
2177 
2178  case 'b':
2179  return RailGraphics->Code_b;
2180 
2181  case 'c':
2182  return RailGraphics->Code_c;
2183 
2184  case 'd':
2185  return RailGraphics->Code_d;
2186 
2187  case 'e':
2188  return RailGraphics->Code_e;
2189 
2190  case 'f':
2191  return RailGraphics->Code_f;
2192 
2193  case 'g':
2194  return RailGraphics->Code_g;
2195 
2196  case 'h':
2197  return RailGraphics->Code_h;
2198 
2199  case 'i':
2200  return RailGraphics->Code_i;
2201 
2202  case 'j':
2203  return RailGraphics->Code_j;
2204 
2205  case 'k':
2206  return RailGraphics->Code_k;
2207 
2208  case 'l':
2209  return RailGraphics->Code_l;
2210 
2211  case 'm':
2212  return RailGraphics->Code_m;
2213 
2214  case 'n':
2215  return RailGraphics->Code_n;
2216 
2217  case 'o':
2218  return RailGraphics->Code_o;
2219 
2220  case 'p':
2221  return RailGraphics->Code_p;
2222 
2223  case 'q':
2224  return RailGraphics->Code_q;
2225 
2226  case 'r':
2227  return RailGraphics->Code_r;
2228 
2229  case 's':
2230  return RailGraphics->Code_s;
2231 
2232  case 't':
2233  return RailGraphics->Code_t;
2234 
2235  case 'u':
2236  return RailGraphics->Code_u;
2237 
2238  case 'v':
2239  return RailGraphics->Code_v;
2240 
2241  case 'w':
2242  return RailGraphics->Code_w;
2243 
2244  case 'x':
2245  return RailGraphics->Code_x;
2246 
2247  case 'y':
2248  return RailGraphics->Code_y;
2249 
2250  case 'z':
2251  return RailGraphics->Code_z;
2252 
2253  default:
2254  return RailGraphics->TempHeadCode;
2255  }
2256 }
2257 
2258 // ----------------------------------------------------------------------------
2259 
2260 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2261 {
2262  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2263  if(Code.Length() != 4)
2264  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2265  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2266  {
2267  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2268  }
2269  if(BackgroundColour != clB5G5R5)
2270  // i.e. not the basic graphic colour as loaded from resource file
2271  {
2272  for(int x = 0; x < 4; x++)
2273  {
2275  }
2276  }
2277  Utilities->CallLogPop(1484);
2278 }
2279 
2280 // ----------------------------------------------------------------------------
2281 
2282 void TTrain::GetLeadElement(int Caller)
2283  // assumes Mid & Lag already set, sets LeadElement,
2284  // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2285 {
2286  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2287  DerailPending = false;
2291  {
2292  // attr 0=straight, - links 0 & 1 (0 = lead)
2293  // attr 1=diverging, - links 2 & 3 (2 = lead)
2294  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2295  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2296 
2297  // if enter at lead, exit at whatever attr set at
2298  // if enter at lag, exit at lead, but set derail wrt attribute
2299  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2300  LeadExitPos = 1;
2301 
2302  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2303  // best to be on safe side
2304  else if(LeadEntryPos == 0)
2305  {
2306  LeadEntryPos = 2;
2307  LeadExitPos = 3;
2308  }
2309  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2310  {
2311  LeadEntryPos = 0;
2312  LeadExitPos = 1;
2313  }
2314  else if(LeadEntryPos == 2)
2315  LeadExitPos = 3;
2316 
2317  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2318  LeadExitPos = 0;
2319  else if(LeadEntryPos == 1)
2320  {
2321  LeadExitPos = 0;
2322  DerailPending = true;
2323  }
2324  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2325  {
2326  LeadExitPos = 0;
2327  DerailPending = true;
2328  }
2329  else if(LeadEntryPos == 3)
2330  LeadExitPos = 0;
2331  }
2332  else if(LeadEntryPos == 0)
2333  LeadExitPos = 1;
2334  else if(LeadEntryPos == 1)
2335  LeadExitPos = 0;
2336  else if(LeadEntryPos == 2)
2337  LeadExitPos = 3;
2338  else if(LeadEntryPos == 3)
2339  LeadExitPos = 2;
2340  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2341 /* signal check moved to Update() function
2342  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2343  && (TrackElement.Attribute == 0))//0 = red
2344  {
2345  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2346  }
2347  else
2348  {
2349  StoppedAtSignal = false;
2350  }
2351 */
2352  Utilities->CallLogPop(662);
2353 }
2354 
2355 // ----------------------------------------------------------------------------
2356 
2357 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2358 {
2359  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2360  switch(Link)
2361  {
2362  case 1:
2363  {
2364  HOffset = 0;
2365  VOffset = 0;
2366  break;
2367  }
2368 
2369  case 2:
2370  {
2371  HOffset = 4;
2372  VOffset = 0;
2373  break;
2374  }
2375 
2376  case 3:
2377  {
2378  HOffset = 8;
2379  VOffset = 0;
2380  break;
2381  }
2382 
2383  case 4:
2384  {
2385  HOffset = 0;
2386  VOffset = 4;
2387  break;
2388  }
2389 
2390  case 6:
2391  {
2392  HOffset = 8;
2393  VOffset = 4;
2394  break;
2395  }
2396 
2397  case 7:
2398  {
2399  HOffset = 0;
2400  VOffset = 8;
2401  break;
2402  }
2403 
2404  case 8:
2405  {
2406  HOffset = 4;
2407  VOffset = 8;
2408  break;
2409  }
2410 
2411  case 9:
2412  {
2413  HOffset = 8;
2414  VOffset = 8;
2415  break;
2416  }
2417 
2418  default:
2419  {
2420  throw Exception("Error in GetOffsetValues - Link value wrong");
2421  }}
2422  Utilities->CallLogPop(674);
2423 }
2424 
2425 // ---------------------------------------------------------------------------
2426 
2427 bool TTrain::LowEntryValue(int EntryLink) const
2428 {
2429 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2430  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2431 */
2432  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2433  return true;
2434  else
2435  return false;
2436 }
2437 
2438 // ---------------------------------------------------------------------------
2439 
2440 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2441 {
2442  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2443  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2444  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2445  // default values
2446  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2447 
2448  TAllRoutes::TRouteType RouteType;
2449 
2450  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2451 
2452  TRect SourceRect, DestRect;
2453 
2454  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2455  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2456  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2457  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2458 
2459  TempGraphic->PixelFormat = pf8bit;
2460  TempGraphic->Width = 16;
2461  TempGraphic->Height = 16;
2462  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2463 
2464  if(TempElement.TrackType == Points)
2465  {
2466  TempGraphic->Assign(TempElement.GraphicPtr);
2467  TempGraphic->Transparent = true;
2468  TempGraphic->TransparentColor = Utilities->clTransparent;
2469  if(RouteType == TAllRoutes::AutoSigsRoute)
2470  {
2471  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2472  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2473  }
2474  else
2475  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2476  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2477  }
2478  else if(TempElement.TrackType == GapJump) // plot set gap
2479  {
2480  if(TempElement.SpeedTag == 88)
2481  TempGraphic->Assign(RailGraphics->gl88set);
2482  else if(TempElement.SpeedTag == 89)
2483  TempGraphic->Assign(RailGraphics->gl89set);
2484  else if(TempElement.SpeedTag == 90)
2485  TempGraphic->Assign(RailGraphics->gl90set);
2486  else if(TempElement.SpeedTag == 91)
2487  TempGraphic->Assign(RailGraphics->gl91set);
2488  else if(TempElement.SpeedTag == 92)
2489  TempGraphic->Assign(RailGraphics->gl92set);
2490  else if(TempElement.SpeedTag == 93)
2491  TempGraphic->Assign(RailGraphics->bm93set);
2492  else if(TempElement.SpeedTag == 94)
2493  TempGraphic->Assign(RailGraphics->bm94set);
2494  else if(TempElement.SpeedTag == 95)
2495  TempGraphic->Assign(RailGraphics->gl95set);
2496  TempGraphic->Transparent = true;
2497  TempGraphic->TransparentColor = Utilities->clTransparent;
2498  if(RouteType == TAllRoutes::AutoSigsRoute)
2499  {
2500  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2501  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2502  }
2503  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2504  }
2505  // new for version 0.6
2506  else if(TempElement.TrackType == SignalPost)
2507  {
2508  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2509  {
2510  for(int x = 0; x < 40; x++)
2511  {
2512  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2513  // need to stop aspect
2514  {
2515  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2516  break;
2517  }
2518  }
2519  }
2520  else // normal signal
2521  {
2522  TempGraphic->Assign(TempElement.GraphicPtr);
2523  // GraphicPtr set to normal signal in a signal track element
2524  }
2525  TempGraphic->Transparent = true;
2526  TempGraphic->TransparentColor = Utilities->clTransparent;
2527  if(RouteType == TAllRoutes::AutoSigsRoute)
2528  {
2529  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2530  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2531  }
2532  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2533  }
2534  else
2535  {
2536  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2537  // can't name points gaps or signals so 'else' OK
2538  bool FoundFlag;
2539  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2540  if(FoundFlag)
2541  {
2543  {
2544  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2545  TempGraphic->Assign(RailGraphics->bmName);
2546  TempGraphic->Transparent = true;
2547  TempGraphic->TransparentColor = Utilities->clTransparent;
2548  if(RouteType == TAllRoutes::AutoSigsRoute)
2549  {
2550  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2551  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2552  }
2553  else
2554  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2555  // draw track on top
2556  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2557  }
2558  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2559  {
2560  TempGraphic->Assign(TempElement.GraphicPtr);
2561  TempGraphic->Transparent = true;
2562  TempGraphic->TransparentColor = Utilities->clTransparent;
2563  // note that can't be an AutoSigsRoute
2564  // now overlay the LC central portion
2565  int BDVectorPos = -1; //not used
2566  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2567  {
2568  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2569  }
2570  else
2571  {
2572  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2573  }
2574  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2575  }
2576  else
2577  {
2578  TempGraphic->Assign(TempElement.GraphicPtr);
2579  TempGraphic->Transparent = true;
2580  TempGraphic->TransparentColor = Utilities->clTransparent;
2581  if(RouteType == TAllRoutes::AutoSigsRoute)
2582  {
2583  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2584  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2585  }
2586  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2587  }
2588  }
2589  else
2590  {
2591  TempGraphic->Assign(TempElement.GraphicPtr);
2592  TempGraphic->Transparent = true;
2593  TempGraphic->TransparentColor = Utilities->clTransparent;
2594  if(RouteType == TAllRoutes::AutoSigsRoute)
2595  {
2596  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2597  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2598  }
2599  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2600  }
2601  }
2602  delete TempGraphic;
2603  Utilities->CallLogPop(675);
2604 }
2605 
2606 // ---------------------------------------------------------------------------
2607 
2608 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2609 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2610 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2611 /*
2612 
2613  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2614  {
2615  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2616  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2617  TAllRoutes::TRouteType RouteType;
2618  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2619  // default values
2620  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2621  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2622  TRect SourceRect, DestRect, ScreenSourceRect;
2623  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2624 
2625  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2626  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2627  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2628 
2629  //add text & user graphics if any to *GraphicPtr prior to adding the track
2630  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2631  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2632  int Right = Left + 8;
2633  int Bottom = Top + 8;
2634  ScreenSourceRect.init(Left, Top, Right, Bottom);
2635  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2636  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2637 
2638  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2639  TempGraphic->PixelFormat = pf8bit;
2640  TempGraphic->Width = 16;
2641  TempGraphic->Height = 16;
2642 
2643  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2644  SourceGraphic->PixelFormat = pf8bit;
2645  SourceGraphic->Width = 16;
2646  SourceGraphic->Height = 16;
2647  SourceGraphic->Transparent = true;
2648  SourceGraphic->TransparentColor = Utilities->clTransparent;
2649 
2650  if (TempElement.TrackType == Points)
2651  {
2652  TempGraphic->Assign(TempElement.GraphicPtr);
2653  TempGraphic->Transparent = true;
2654  TempGraphic->TransparentColor = Utilities->clTransparent;
2655  if (RouteType == TAllRoutes::AutoSigsRoute)
2656  {
2657  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2658  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2659  }
2660  else
2661  {
2662  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2663  }
2664  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2665  }
2666  else if (TempElement.TrackType == GapJump) // plot set gap
2667  {
2668  if (TempElement.SpeedTag == 88)
2669  TempGraphic->Assign(RailGraphics->gl88set);
2670  else if (TempElement.SpeedTag == 89)
2671  TempGraphic->Assign(RailGraphics->gl89set);
2672  else if (TempElement.SpeedTag == 90)
2673  TempGraphic->Assign(RailGraphics->gl90set);
2674  else if (TempElement.SpeedTag == 91)
2675  TempGraphic->Assign(RailGraphics->gl91set);
2676  else if (TempElement.SpeedTag == 92)
2677  TempGraphic->Assign(RailGraphics->gl92set);
2678  else if (TempElement.SpeedTag == 93)
2679  TempGraphic->Assign(RailGraphics->bm93set);
2680  else if (TempElement.SpeedTag == 94)
2681  TempGraphic->Assign(RailGraphics->bm94set);
2682  else if (TempElement.SpeedTag == 95)
2683  TempGraphic->Assign(RailGraphics->gl95set);
2684  TempGraphic->Transparent = true;
2685  TempGraphic->TransparentColor = Utilities->clTransparent;
2686  if (RouteType == TAllRoutes::AutoSigsRoute) {
2687  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2688  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2689  }
2690  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2691  }
2692  // new for version 0.6
2693  else if (TempElement.TrackType == SignalPost)
2694  {
2695  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2696  {
2697  for (int x = 0; x < 40; x++)
2698  {
2699  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2700  // need to stop aspect
2701  {
2702  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2703  break;
2704  }
2705  }
2706  }
2707  else // normal signal
2708  {
2709  TempGraphic->Assign(TempElement.GraphicPtr);
2710  // GraphicPtr set to normal signal in a signal track element
2711  }
2712  TempGraphic->Transparent = true;
2713  TempGraphic->TransparentColor = Utilities->clTransparent;
2714  if (RouteType == TAllRoutes::AutoSigsRoute) {
2715  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2716  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2717  }
2718  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2719  }
2720  else {
2721  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2722  // can't name points gaps or signals so 'else' OK
2723  bool FoundFlag;
2724  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2725  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2726  if (FoundFlag)
2727  {
2728  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2729  {
2730  GraphicPtr->Canvas->CopyRect(DestRect,
2731  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2732  TempGraphic->Assign(RailGraphics->bmName);
2733  TempGraphic->Transparent = true;
2734  TempGraphic->TransparentColor = Utilities->clTransparent;
2735  if (RouteType == TAllRoutes::AutoSigsRoute)
2736  {
2737  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2738  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2739  }
2740  else
2741  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2742  // draw track on top
2743  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2744  SourceRect);
2745  }
2746  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2747  TempGraphic->Assign(TempElement.GraphicPtr);
2748  TempGraphic->Transparent = true;
2749  TempGraphic->TransparentColor = Utilities->clTransparent;
2750  // note that can't be an AutoSigsRoute
2751  // now overlay the LC central portion
2752  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2753  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2754  SourceRect);
2755  }
2756  else {
2757  TempGraphic->Assign(TempElement.GraphicPtr);
2758  TempGraphic->Transparent = true;
2759  TempGraphic->TransparentColor = Utilities->clTransparent;
2760  if (RouteType == TAllRoutes::AutoSigsRoute) {
2761  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2762  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2763  }
2764  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2765  SourceRect);
2766  }
2767  }
2768  else {
2769  TempGraphic->Assign(TempElement.GraphicPtr);
2770  TempGraphic->Transparent = true;
2771  TempGraphic->TransparentColor = Utilities->clTransparent;
2772  if (RouteType == TAllRoutes::AutoSigsRoute) {
2773  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2774  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2775  }
2776  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2777  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2778  }
2779  }
2780  delete TempGraphic;
2781  delete SourceGraphic;
2782  Utilities->CallLogPop();
2783  }
2784 */
2785 // ---------------------------------------------------------------------------
2786 
2787 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2788 {
2789  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2790  if(PlotElement[ArrayNumber] == -1)
2791  {
2792  Utilities->CallLogPop(676);
2793  return; // not plotted yet
2794  }
2795  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2796  // set before plot so gap flashing stops first
2797  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2798  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2799  // Only need to set ID for leading element, stays set until train finally leaves the element
2800  Plotted = true;
2801  Utilities->CallLogPop(677);
2802 }
2803 
2804 // ---------------------------------------------------------------------------
2805 
2806 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2807 {
2808  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2809  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2810 }
2811 
2812 // ---------------------------------------------------------------------------
2813 
2814 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2815 {
2816  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2817  HeadCode);
2818  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2819  {
2820  Utilities->CallLogPop(678);
2821  return true;
2822  }
2823  else
2824  {
2825  Utilities->CallLogPop(679);
2826  return false;
2827  }
2828 }
2829 
2830 // ---------------------------------------------------------------------------
2831 
2832 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2833 {
2834  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2835  "," + HeadCode);
2836  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2837  {
2838  Utilities->CallLogPop(680);
2839  return true;
2840  }
2841  else
2842  {
2843  Utilities->CallLogPop(681);
2844  return false;
2845  }
2846 }
2847 
2848 // ---------------------------------------------------------------------------
2849 
2850 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
2851  // test whether this train on a bridge on trackpos 0 & 1
2852 {
2853  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
2854  HeadCode);
2855  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
2856  {
2857  Utilities->CallLogPop(682);
2858  return false;
2859  }
2860  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2861  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
2862  {
2863  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2864  {
2865  throw Exception("Error, same train on two different bridge tracks");
2866  }
2867  else
2868  {
2869  Utilities->CallLogPop(684);
2870  return true;
2871  }
2872  }
2873  else
2874  {
2875  Utilities->CallLogPop(685);
2876  return false;
2877  }
2878 }
2879 
2880 // ---------------------------------------------------------------------------
2881 
2882 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
2883  // test whether this train on a bridge on trackpos 2 & 3
2884 {
2885  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
2886  HeadCode);
2887  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
2888  {
2889  Utilities->CallLogPop(686);
2890  return false;
2891  }
2892  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
2893  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
2894  {
2895  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
2896  Utilities->CallLogPop(687);
2897  return true;
2898  }
2899  else
2900  {
2901  Utilities->CallLogPop(688);
2902  return false;
2903  }
2904 }
2905 
2906 // ---------------------------------------------------------------------------
2907 
2908 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2909 {
2910  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2911  AnsiString(EntryPos) + "," + HeadCode);
2912  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
2913 
2914  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
2915  if(Track->GapFlashFlag)
2916  {
2918  {
2921  Track->GapFlashFlag = false;
2922  }
2923  }
2924 
2925  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
2926  {
2927  if(EntryPos == -1)
2928  {
2929  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
2930  }
2931  if(EntryPos < 2)
2932  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
2933  else
2934  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
2935  }
2936  Utilities->CallLogPop(690);
2937 }
2938 
2939 // ---------------------------------------------------------------------------
2940 
2941 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
2942 {
2943  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
2944  AnsiString(EntryPos) + "," + HeadCode);
2945  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
2946  {
2947  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
2948  }
2949  else
2950  {
2951  if(EntryPos == -1)
2952  {
2953  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
2954  }
2955  if(EntryPos < 2)
2956  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
2957  else
2958  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
2959  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
2960  // i.e. other train on track 2&3
2961  {
2962  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
2963  }
2964  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
2965  // i.e. other train on track 1&2
2966  {
2967  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
2968  }
2969  else
2970  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
2971  }
2972  Utilities->CallLogPop(691);
2973 }
2974 
2975 // ---------------------------------------------------------------------------
2976 
2977 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
2978 {
2979  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
2980  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
2981  int LockedVectorNumber;
2982 
2983  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
2984  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
2985  { // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
2986  Utilities->CallLogPop(692);
2987  return;
2988  }
2989  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
2990  // i.e other track is in a marked route
2991  // LinkPos doesn't have to be the entry position for the above check
2992  {
2993  TRect SourceRect, DestRect;
2994  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
2995  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2996  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2997  // identify the route element for the other track
2998  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
2999  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3000  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3001  int FirstELink, SecondELink = -1;
3002  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3003  // must be at least one
3004  if(RoutePair2.first > -1)
3005  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3006  TPrefDirElement RouteElement;
3007  // Graphics::TBitmap *RouteGraphic;
3008  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3009  // i.e. other track is in RoutePair2
3010  {
3011  if(SecondELink == -1)
3012  {
3013  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3014  }
3015  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3016  // error if both have same Link number
3017  {
3018  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3019  }
3020  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3021  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3022  }
3023  else // other track is in RoutePair1
3024  {
3025  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3026  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3027  }
3028  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3029  DestGraphic->PixelFormat = pf8bit;
3030  DestGraphic->Width = 8;
3031  DestGraphic->Height = 8;
3032  DestGraphic->Transparent = true;
3033  // has to be transparent or will overwrite the track that the train has just left
3034  DestGraphic->TransparentColor = Utilities->clTransparent;
3035  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3036  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3037  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3038  // plot locked route marker for other route if appropriate
3039  TPrefDirElement PrefDirElement; // holder for next call, unused
3040  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3041  if(StraddleValue == LeadMidLag)
3042  {
3044  PrefDirElement, LockedVectorNumber))
3045  {
3046  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3047  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3048  }
3049  }
3050  delete DestGraphic;
3051  }
3052  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3053  // also can only be a bridge or trains either have already or soon will crash
3054  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3055  {
3056  Utilities->CallLogPop(695);
3057  return;
3058  }
3059  if(ElementEntryPos > 1) // other train is on track 01
3060  {
3061  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3062  {
3064  }
3065  }
3066  else // other train is on track 23
3067  {
3068  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3069  {
3071  }
3072  }
3073  Utilities->CallLogPop(696);
3074 }
3075 
3076 // ---------------------------------------------------------------------------
3077 
3078 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3079 {
3080  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3081  AnsiString(EntryPos) + "," + HeadCode);
3082  int RouteNumber;
3083  bool WrongRoute = false;
3084  TPrefDirElement RouteElement;
3086  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3087 
3088  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3089  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3090  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3091  {
3092  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3093  {
3094  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3095  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3096  {
3097  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3098  { // don't call for stub end routes
3100  }
3101  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3102  Utilities->CallLogPop(697);
3103  return;
3104  }
3105  }
3106  // also need to check for a route on a crossing diagonal
3107  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3108  int LinkNumber = TrackElement.Link[EntryPos];
3109  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3110  {
3111  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3112  {
3113  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3114  bool LogActionErrorCalled = false;
3115  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3116  if(LinkNumber == 1)
3117  {
3118  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3119  {
3120  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3121  { // don't call for stub end routes
3123  LogActionErrorCalled = true;
3124  }
3125  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3126  }
3127  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3128  // not else in case have different routes on each diagonal, though shouldn't be possible
3129  {
3130  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3131  { // don't call for stub end routes
3133  }
3134  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3135  }
3136  }
3137 
3138  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3139  else if(LinkNumber == 3)
3140  {
3141  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3142  {
3143  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3144  { // don't call for stub end routes
3146  LogActionErrorCalled = true;
3147  }
3148  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3149  }
3150  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3151  // not else in case have different routes on each diagonal, though shouldn't be possible
3152  {
3153  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3154  { // don't call for stub end routes
3156  }
3157  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3158  }
3159  }
3160 
3161  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3162  else if(LinkNumber == 7)
3163  {
3164  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3165  {
3166  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3167  { // don't call for stub end routes
3169  LogActionErrorCalled = true;
3170  }
3171  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3172  }
3173  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3174  // not else in case have different routes on each diagonal, though shouldn't be possible
3175  {
3176  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3177  { // don't call for stub end routes
3179  }
3180  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3181  }
3182  }
3183 
3184  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3185  else if(LinkNumber == 9)
3186  {
3187  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3188  {
3189  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3190  { // don't call for stub end routes
3192  LogActionErrorCalled = true;
3193  }
3194  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3195  }
3196  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3197  // not else in case have different routes on each diagonal, though shouldn't be possible
3198  {
3199  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3200  { // don't call for stub end routes
3202  }
3203  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3204  }
3205  }
3206  }
3207  }
3208  Utilities->CallLogPop(698);
3209  return; // no route on other track or no other track
3210  }
3211  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3212  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3213  {
3214  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3215  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new after v2.4.1 for points check - Xeon repoted it 30/05/20. He found that for routes that
3216  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3217  {
3218  if(RouteElement.GetELinkPos() == EntryPos)
3219  {
3220  Utilities->CallLogPop(699);
3221  return; // right direction
3222  }
3223  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3224  {
3225  Utilities->CallLogPop(700);
3226  return; // right direction (points)
3227  }
3228  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3229  {
3230  Utilities->CallLogPop(701);
3231  return; // right direction (points)
3232  }
3233  else if(RouteElement.GetXLinkPos() == EntryPos)
3234  {
3235  WrongRoute = true;
3236  break; // wrong direction
3237  }
3238  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3239  {
3240  WrongRoute = true;
3241  break; // wrong direction
3242  }
3243  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3244  {
3245  WrongRoute = true;
3246  break; // wrong direction
3247  }
3248  }
3249  }
3250  if(!WrongRoute)
3251  {
3252  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3253  }
3254  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3255  { // don't call for stub end routes
3257  }
3258  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3259  Utilities->CallLogPop(703);
3260 }
3261 
3262 // ---------------------------------------------------------------------------
3263 
3264 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3265 {
3266  if(BackgroundColour == NewBackgroundColour)
3267  return; // don't replot if already correct
3268 
3269  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3270  bool ColourError = false, ColourError2 = false;
3271 
3272  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3273  if(ColourError)
3274  ColourError2 = true;
3275  for(int x = 0; x < 4; x++)
3276  {
3277  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3278  if(ColourError)
3279  ColourError2 = true;
3280  }
3281  if(ColourError2)
3282  {
3284  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3285  }
3286 
3287  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3288  // of motion
3289  for(int x = 0; x < 4; x++)
3290  {
3291  PlotTrainGraphic(6, x, Disp);
3292  }
3293  BackgroundColour = NewBackgroundColour;
3294  Display->Update();
3295  // need to keep this since Update() not called for PlotSmallOutput as too slow
3296  Utilities->CallLogPop(704);
3297 }
3298 
3299 // ---------------------------------------------------------------------------
3300 
3301 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3302 /*
3303 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3304 
3305 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3306 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3307 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3308 full-element moves.
3309 
3310 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3311 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3312 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3313 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3314 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3315 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3316 In this case set the brake rate to maximum to stop as soon as possible.
3317 
3318 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3319 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3320 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3321 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3322 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3323 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3324 
3325 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3326 first to see whether buffers or continuation) in turn is examined: first the length of the
3327 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3328 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3329 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3330 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3331 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3332 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3333 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3334 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3335 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3336 siding then again emeregency braking may be necessary and a crash may result.
3337 
3338 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3339 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3340 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3341 buffer, then the train accelerates for half the element and brakes for the other half.
3342 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3343 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3344 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3345 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3346 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3347 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3348 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3349 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3350 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3351 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3352 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3353 
3354 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3355 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3356 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3357 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3358 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3359 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3360 MaxBrakeRate.
3361 
3362 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3363 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3364 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3365 
3366 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3367 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3368 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3369 
3370 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3371 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3372 when Straddle == LeadMidLag
3373 */
3374 {
3375  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3376  AnsiString(EntryPos) + "," + HeadCode);
3377  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3378  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3379  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3380  TrainInFrontInSignallerModeFlag = false;
3381  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3382  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3383  bool SignallerStopRequired = false;
3384 
3386  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3387 
3388  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3389 
3390  OneLengthAccelDecel = false;
3391  BrakeRate = 0;
3392 
3393 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3394  if(CurrentTrackVectorPosition > -1)
3395  {
3396  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3397  {
3398  if((EntryPos == 0) || (EntryPos == 2))
3399  {
3400  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3401  {
3402  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3403  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3404  }
3405  else
3406  {
3407  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3408  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3409  }
3410  }
3411  else if(EntryPos == 1)
3412  {
3413  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3414  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3415  }
3416  else // == 3
3417  {
3418  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3419  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3420  }
3421  }
3422  else
3423  {
3424  if(EntryPos > 1)
3425  {
3426  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3427  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3428  }
3429  else
3430  {
3431  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3432  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3433  }
3434  }
3435  EntryHalfLength = CurrentElementHalfLength;
3436  FrontElementLength = 2 * CurrentElementHalfLength;
3437  }
3438  else
3439  {
3440  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3441  }
3442  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3443  {
3444  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3445  }
3446 
3447  // check if zero entry speed with another train directly in front & if so remain stopped
3448  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3449  {
3450  EntrySpeed = 0;
3451  ExitSpeedHalf = 0;
3452  ExitSpeedFull = 0;
3453  MaxExitSpeed = 0;
3454  BrakeRate = 0;
3455  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3456  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3457  StoppedForTrainInFront = true;
3458  Utilities->CallLogPop(705);
3459  return;
3460  }
3461 
3462  // new at v2.4.0 - check for stopped and zero power
3463  if((EntrySpeed < 1) && PowerAtRail < 1)
3464  {
3465  EntrySpeed = 0;
3466  ExitSpeedHalf = 0;
3467  ExitSpeedFull = 0;
3468  MaxExitSpeed = 0;
3469  BrakeRate = 0;
3470  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3471  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3472  StoppedWithoutPower = true;
3473  Utilities->CallLogPop(2125);
3474  return;
3475  }
3476 
3477 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3478  if(BeingCalledOn)
3479  {
3480  LimitingSpeed = CallOnMaxSpeed;
3481  }
3482  else
3483  {
3484  LimitingSpeed = MaximumSpeedLimit;
3485  }
3486  if(LimitingSpeed > FrontElementSpeedLimit)
3487  LimitingSpeed = FrontElementSpeedLimit;
3488  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3489  LimitingSpeed = MaxRunningSpeed;
3490  FrontElementMaxSpeed = LimitingSpeed;
3491 
3492 /*
3493  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3494  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3495  (2) V/3.6 = U/3.6 - FT;
3496  (3) S = UT/3.6 - 0.5FT^2
3497  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3498 
3499  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3500  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3501  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3502  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3503  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3504 
3505  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3506  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3507  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3508 */
3509 
3510 // check if running past a red signal without permission
3511  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3512  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3513  {
3514  SPADFlag = true; // user has to intervene to reset & restart after spad
3515  }
3516 
3517  if(!SPADFlag)
3518  {
3519  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3520 
3521  double ExitSpeedAtMaxBraking;
3522  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3523  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3525  {
3526  ExitSpeedAtMaxBraking = 0;
3527  }
3528  else
3529  {
3530  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3531  }
3532  double SpeedToUse;
3533  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added after v2.4.1 because trains entering at a continuation with zero (or very low) speed
3534  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3535  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3536  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3537  {
3538  SpeedToUse = ExitSpeedAtMaxBraking;
3539  }
3540  else
3541  {
3542  SpeedToUse = LimitingSpeed;
3543  }
3544  if(ExitSpeedFull > SpeedToUse)
3545  {
3546  ExitSpeedFull = SpeedToUse;
3547  }
3548  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3549  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3550 
3551  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3552  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3553  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3554 
3555  do
3556  {
3557  RedSignalFlag = false;
3558  BuffersFlag = false;
3559  StationFlag = false;
3560  BuffersOrContinuationNowFlag = false;
3561  ContinuationNextFlag = false;
3562  // have to reset this after the above test
3563  // add current element length to CumulativeLength
3564  CumulativeLength += (2 * CurrentElementHalfLength);
3565  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3566  {
3567  SignallerStopRequired = true;
3568  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3569  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3570  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3571  if(SignallerStopBrakeRate < TempBR)
3572  {
3573  SignallerStopBrakeRate = TempBR;
3574  }
3575  }
3576  // first check for stops within the length of the current element, where don't want any more checks & don't want
3577  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3578  // during the last loop when the NextTrackVectorPosition was the signal.
3579 
3580  // check if current element is a buffer
3581  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3582  {
3583  // no need to add in the length of this element to CumulativeLength as already included
3584  BuffersFlag = true;
3585  }
3586  // check if current element is a station stop
3587  if(TrainMode == Timetable)
3588  {
3589  bool StopRequired = false;
3590  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3591  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3592  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3593  {
3594  // no need to add in the length of element to CumulativeLength
3595  if(StopRequired)
3596  {
3597  StationFlag = true;
3598  }
3599  }
3600  }
3601  else
3602  {
3603  StationFlag = false;
3604  }
3605 
3606  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3607  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3608  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3609  {
3610  BuffersOrContinuationNowFlag = true;
3611  }
3612  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3613  {
3614  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3615  {
3616  if((EntryPos == 0) || (EntryPos == 2))
3617  {
3618  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3619  ExitPos = 1;
3620  else
3621  ExitPos = 3;
3622  }
3623  else
3624  ExitPos = 0;
3625  }
3626  else
3627  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3628  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3629  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3630  if(NextTrackVectorPosition > -1)
3631  {
3632  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3633  // this test & section added at v0.6
3634  {
3635  if((NextEntryPos == 0) || (NextEntryPos == 2))
3636  {
3637  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3638  {
3639  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3640  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3641  }
3642  else
3643  {
3644  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3645  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3646  }
3647  }
3648  else if(NextEntryPos == 1)
3649  {
3650  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3651  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3652  }
3653  else // == 3
3654  {
3655  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3656  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3657  }
3658  }
3659  else
3660  {
3661  if(NextEntryPos > 1)
3662  {
3663  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3664  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3665  }
3666  else
3667  {
3668  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3669  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3670  }
3671  }
3672  }
3673  else
3674  {
3675  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3676  }
3677  // now check for stops, first cover those where don't want to add in length of next element
3678  // check if next element is a red signal - Attr 0,
3679  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3680  // CurrentTrackVectorPosition not NextTrackVectorPosition
3681  bool StopRequired;
3682  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3683  {
3684  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3685  // no need to add in the length of element to CumulativeLength
3686  RedSignalFlag = true;
3687  // next element is a red signal
3688  }
3689  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3690  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3691  // at least one platform element free
3693  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3694  NextEntryPos, TrainID))
3695  {
3696  // no need to add in the length of element to CumulativeLength
3697  if(StopRequired)
3698  StationFlag = true;
3699  }
3700  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3701  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3702  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3703  {
3704  // no need to add in the length of element to CumulativeLength
3705  TrainInFrontInSignallerModeFlag = true;
3706  }
3707  // check if next element is a buffer
3708  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3709  {
3710  // need to add in the length of that element to CumulativeLength
3711  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3712  BuffersFlag = true;
3713  }
3714  // check if next element is a station stop
3716  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3717  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3718  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3719  {
3720  // need to add in the length of that element to CumulativeLength if a stop required
3721  if(StopRequired)
3722  {
3723  StationFlag = true;
3724  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3725  }
3726  }
3727  }
3728  //now can decide whether need to stop over CumulativeLength
3729  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3730  {
3731  // have to come to a stop over CumulativeLength
3732  if(CumulativeLength == FrontElementLength)
3733  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3734  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3735  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3736  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3737  // if not calc speed at halfway point & if less than above set half speed to this value;
3738  // use constant acceleration in calculating half time point
3739  {
3740  MaxExitSpeed = 0;
3741  double MaxHalfSpeed;
3742  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3743  // have to halve the element length, & can't be zero or negative so no need to test
3744  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3745  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3746  MaxHalfSpeed = FrontElementMaxSpeed;
3747  else
3748  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3749  if(MaxHalfSpeed > (2 * EntrySpeed))
3750  // use 2x to prevent kangarooing at last element when had
3751  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3752  {
3753  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3754  0.333334);
3755  bool HalfSpeedLimited = false;
3756  if(MaxHalfSpeed < ExitSpeedHalf)
3757  {
3758  ExitSpeedHalf = MaxHalfSpeed;
3759  HalfSpeedLimited = true;
3760  }
3761  if(PowerAtRail > 1)
3762  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3763  { // [km/h/3.6 = m/s]
3764  ExitTimeHalf =
3765  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3766  }
3767  else
3768  {
3769  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3770  }
3771  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3772  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3773  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3774  // by a long braking period
3775  ExitSpeedFull = 0;
3776  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3777  if(TempBrakeRate > MaxBrakeRate)
3778  TempBrakeRate = MaxBrakeRate;
3779  // shouldn't be but leave in anyway
3780  if(TempBrakeRate > BrakeRate)
3781  BrakeRate = TempBrakeRate;
3782  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3783  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3784  if(HalfSpeedLimited)
3785  // this is the change referred to above
3786  {
3787  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
3788  ExitTimeHalf = EntryTime + BrakingTime;
3789  ExitTimeFull = ExitTimeHalf + BrakingTime;
3790  }
3791  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
3792  Utilities->CallLogPop(1095);
3793  return;
3794  }
3795  }
3796  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
3797  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
3798  // calc th, tf, sh, & sf
3799  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3800  if(TempBrakeRate > MaxBrakeRate)
3801  TempBrakeRate = MaxBrakeRate;
3802  if(TempBrakeRate > BrakeRate)
3803  BrakeRate = TempBrakeRate;
3804  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3805  if(SignallerStopRequired)
3806  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
3807  {
3809  {
3811  // this prevents the brakerate from reducing for a signaller stop
3812  // regardless of other conditions that may change as progress round the loop
3813  }
3814  }
3816  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
3817  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
3818  {
3820  }
3821  int TempMaxExitSpeed;
3822  // calc current value & if less than MaxExitSpeed set that to this
3823  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
3824  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3825  MaxExitSpeedAtHalfBraking = 0;
3826  else
3827  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3828  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
3829  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
3830  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3831  TempMaxExitSpeed = FrontElementMaxSpeed;
3832  else
3833  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3834  if(TempMaxExitSpeed < MaxExitSpeed)
3835  MaxExitSpeed = TempMaxExitSpeed;
3836 
3837  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
3838  // Cumulativelength, and Cumulativelength
3839 
3840  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
3841  {
3842  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
3843  if(ExitSpeedHalfSquared < 10)
3844  ExitSpeedHalf = 0;
3845  else
3846  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3847  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
3848  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
3849  if(ExitSpeedFullSquared < 10)
3850  ExitSpeedFull = 0;
3851  else
3852  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
3853  if((StationFlag) && (CumulativeLength == FrontElementLength))
3854  {
3855  ExitSpeedFull = 0;
3856  // force a stop for station (not for buffers or red signal)
3857  }
3858  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
3859  }
3860  // new condition at v2.4.0
3861  else if(PowerAtRail <= 1)
3862  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
3863  // avoid using AValue in denominator or have excessively long times
3864  {
3865  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
3866  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
3867  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
3868 
3869  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
3870  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
3871  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
3872  }
3873  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
3874  // without the power need above condition or have hours of delay times, above added at v2.4.0
3875  {
3876  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3877  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3878  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3879  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3880  BrakeRate = 0;
3881  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3882  0.333334);
3883  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3884  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
3886  // can accelerate continually over the half length
3887  {
3888  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
3889  / 86400.0);
3891  // can accelerate continually over the full length
3892  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
3893  {
3894  ExitTimeFull =
3895  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3896  }
3897  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
3898  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
3899  {
3900  // added at v0.6 as a safeguard
3901  if(MaxExitSpeed < EntrySpeed)
3903  // to prevent DeltaExitTimeToMaxInSecs being negative
3904  if(MaxExitSpeed < 1)
3905  MaxExitSpeed = 1;
3906  // to prevent divide by zero error
3907  // below as was
3909  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
3910  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
3911  (1.5 * AValue * AValue);
3912  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
3913  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
3914  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
3915  }
3916  }
3917  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
3918  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
3919  // second halves of the element
3920  {
3921  // added at v0.6 as a safeguard
3922  if(MaxExitSpeed < EntrySpeed)
3924  // to prevent DeltaExitTimeToMaxInSecs being negative
3925  if(MaxExitSpeed < 1)
3926  MaxExitSpeed = 1; // to prevent divide by zero error
3927  // below as was
3929  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
3930  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
3931  (1.5 * AValue * AValue);
3932  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
3933  // remaining distance to half length
3934  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
3935  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
3937  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
3938  }
3939  }
3940  Utilities->CallLogPop(706);
3941  return;
3942  }
3943  else
3944  {
3945  if(!BuffersOrContinuationNowFlag)
3946  {
3947  if(NextSpeedLimit < LimitingSpeed)
3948  LimitingSpeed = NextSpeedLimit;
3949  }
3950  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
3951  int TempMaxExitSpeed;
3952  // calc current value & if less than MaxExitSpeed set that to this
3953  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
3954  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
3955  if(MaxExitSpeedAtHalfBrakingSquared < 10)
3956  MaxExitSpeedAtHalfBraking = 0;
3957  else
3958  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
3959  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
3960  TempMaxExitSpeed = FrontElementMaxSpeed;
3961  else
3962  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
3963  if(TempMaxExitSpeed < MaxExitSpeed)
3964  MaxExitSpeed = TempMaxExitSpeed;
3965  // MaxExitSpeed is an external variable & this can reduce its value
3966  if(EntrySpeed > LimitingSpeed)
3967  // note that LimitingSpeed is more restrictive than MaxExitSpeed
3968  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
3969  {
3970  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
3971  if(TempBrakeRate > MaxBrakeRate)
3972  TempBrakeRate = MaxBrakeRate;
3973  // shouldn't be for speedlimits since all known in advance, but include anyway
3974  if(TempBrakeRate > BrakeRate)
3975  BrakeRate = TempBrakeRate;
3976  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3977  }
3978  }
3979  if(!BuffersOrContinuationNowFlag)
3980  {
3981  CurrentTrackVectorPosition = NextTrackVectorPosition;
3982  EntryPos = NextEntryPos;
3983  CurrentElementHalfLength = NextElementHalfLength;
3984  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
3985  {
3986  ContinuationNextFlag = true;
3987  }
3988  }
3989  }
3990  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
3992  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
3993  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
3994  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
3995  // stopping distance after it.
3996 
3997  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
3998  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
3999 
4000  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4001  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4002  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4003  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4004  // too late
4005 
4006  // set final braking or acc'n speed & time values
4007  if(BrakeRate > 0.01)
4008  {
4009  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4010  if(ExitSpeedHalfSquared < 10)
4011  ExitSpeedHalf = 0;
4012  else
4013  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4014  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4015  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4016  if(ExitSpeedFullSquared < 10)
4017  ExitSpeedFull = 0;
4018  else
4019  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4020  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4021  }
4022  else
4023  {
4024  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4025  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4026  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4027  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4028 
4029  BrakeRate = 0;
4030  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4031  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4032  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4034  {
4035  if(PowerAtRail > 1)
4036  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4037  { // [km/h/3.6 = m/s]
4038  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4039  / 86400.0);
4040  }
4041  else
4042  {
4043  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4044  }
4045 
4047  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4048  {
4049  if(PowerAtRail > 1)
4050  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4051  { // [km/h/3.6 = m/s]
4052  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4053  / 86400.0);
4054  }
4055  else
4056  {
4057  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4058  }
4059  }
4060  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4061  {
4062  // added at v0.6 as a safeguard
4063  if(MaxExitSpeed < EntrySpeed)
4065  // to prevent DeltaExitTimeToMaxInSecs being negative
4066  if(MaxExitSpeed < 1)
4067  MaxExitSpeed = 1; // to prevent divide by zero error
4068  // below as was
4070  double DeltaExitTimeToMaxInSecs;
4071  double DistanceToMax;
4072  if(PowerAtRail > 1) // added at v2.4.0
4073  {
4074  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4075  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4076  (1.5 * AValue * AValue);
4077  }
4078  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4079  {
4080  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4081  // these not really accurate but will be good enough
4082  DistanceToMax = EntryHalfLength;
4083  }
4084  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4085  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4086  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4087  }
4088  }
4089  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4090  {
4091  // added at v0.6 as a safeguard
4092  if(MaxExitSpeed < EntrySpeed)
4094  // to prevent DeltaExitTimeToMaxInSecs being negative
4095  if(MaxExitSpeed < 1)
4096  MaxExitSpeed = 1; // to prevent divide by zero error
4097  // below as was
4099  double DeltaExitTimeToMaxInSecs;
4100  double DistanceToMax;
4101  if(PowerAtRail > 1) // added at v2.4.0
4102  {
4103  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4104  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4105  (1.5 * AValue * AValue);
4106  }
4107  else
4108  {
4109  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4110  // these not really accurate but will be good enough
4111  DistanceToMax = EntryHalfLength / 2;
4112  }
4113  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4114  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4115  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4117  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4118  }
4119  }
4120  }
4121 
4122  else // SPADFlag set
4123  {
4125  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4126  if(ExitSpeedHalfSquared < 10)
4127  ExitSpeedHalf = 0;
4128  else
4129  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4130  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4131  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4132  if(ExitSpeedFullSquared < 10)
4133  ExitSpeedFull = 0;
4134  else
4135  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4136  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4137 
4138  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4139  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4140  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4141  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4142  // will be the stopping speed.
4143  if(ExitSpeedFull > 0)
4144  {
4145  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4146  {
4147  if((EntryPos == 0) || (EntryPos == 2))
4148  {
4149  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4150  ExitPos = 1;
4151  else
4152  ExitPos = 3;
4153  }
4154  else
4155  ExitPos = 0;
4156  }
4157  else
4158  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4159  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4160  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4161  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4162  {
4163  int NextElementLength;
4164  if(NextEntryPos > 1)
4165  {
4166  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4167  }
4168  else
4169  {
4170  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4171  }
4172  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4173  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4174  {
4175  ExitSpeedFull = 0;
4176  }
4177  }
4178  }
4179  }
4180  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4181  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4182  { // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4183  if(LeadElement > -1)
4184  {
4186  // don't stop on a continuation either entering or leaving
4187  {
4190  MaxExitSpeed = LimitingSpeed;
4191  BrakeRate = 0;
4192  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4193  // assume length is 50m for ease of calc
4194  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4195  Utilities->CallLogPop(2126);
4196  return;
4197  }
4198  }
4199  else if(MidElement > -1)
4200  {
4202  {
4205  MaxExitSpeed = LimitingSpeed;
4206  BrakeRate = 0;
4207  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4208  // assume length is 50m for ease of calc
4209  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4210  Utilities->CallLogPop(2127);
4211  return;
4212  }
4213  }
4214  else if(LagElement > -1)
4215  {
4217  {
4220  MaxExitSpeed = LimitingSpeed;
4221  BrakeRate = 0;
4222  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4223  // assume length is 50m for ease of calc
4224  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4225  Utilities->CallLogPop(2128);
4226  return;
4227  }
4228  }
4229 
4230  if(EntrySpeed > 7.5) // keep going for at least another element
4231  {
4232  ExitSpeedHalf = EntrySpeed - 2.5;
4233  ExitSpeedFull = EntrySpeed - 5;
4234  MaxExitSpeed = LimitingSpeed;
4235  BrakeRate = 0;
4236  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4237  // assume length is 50m for ease of calc
4238  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4239  Utilities->CallLogPop(2129);
4240  return;
4241  }
4242  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4243  { // will appear to have slowed at steady rate
4244  ExitSpeedHalf = 0;
4245  ExitSpeedFull = 0;
4246  MaxExitSpeed = LimitingSpeed;
4247  BrakeRate = 0;
4248  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4249  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4250  StoppedWithoutPower = true;
4251  Utilities->CallLogPop(2130);
4252  return;
4253  }
4254  }
4255 
4256  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4257  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4258  Utilities->CallLogPop(707);
4259 }
4260 // ---------------------------------------------------------------------------
4261 /*
4262  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4263  {
4264  int NextExitPos;
4265  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4266  if(TimetableVector.empty()) return false;
4267  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4268  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4269  {
4270  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4271  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4272  {
4273  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4274  }
4275  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4276  {
4277  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4278  }
4279  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4280  NextElement = TempElement;
4281  }
4282  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4283  if(NextElement.TrackType == Buffers) return true;
4284  return false;//shouldn't reach here but include to prevent compiler return warning
4285  }
4286 */
4287 // ---------------------------------------------------------------------------
4288 
4289 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4290 /*
4291  returns the number by which the train ActionVectorEntryPtr needs
4292  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4293  actions when a stop or pass location has been reached before other timetabled actions have been carried out. If can't find it, or Name
4294  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4295  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4296  or pass (false) the location.
4297 */ {
4298  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4299  Stop = false;
4300  if(TimetableFinished || (Name == ""))
4301  {
4302  Utilities->CallLogPop(957);
4303  return -1;
4304  }
4305  // start looking from current pointer position
4306  for(TActionVectorEntry * Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4307  {
4308  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4309  {
4310  break;
4311  }
4312  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4313  {
4314  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4315  {
4316  Stop = true;
4317  Utilities->CallLogPop(960);
4318  return (Ptr - ActionVectorEntryPtr);
4319  }
4320  }
4321  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4322  {
4323  Utilities->CallLogPop(1517);
4324  return (Ptr - ActionVectorEntryPtr);
4325  }
4326  }
4327  Utilities->CallLogPop(959);
4328  return -1; // not found a valid entry
4329 }
4330 
4331 // ---------------------------------------------------------------------------
4332 
4334 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4335  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4336  Ignores the call-on signal.
4337 */ {
4338  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4339  int ReturnVal = 0;
4340 
4341  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4342  {
4343  Track->TrackVector.at(x).TempTrackMarker01 = false;
4344  Track->TrackVector.at(x).TempTrackMarker23 = false;
4345  }
4346  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4347  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4348 
4349  while(true)
4350  {
4351  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4352  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4353  {
4354  ReturnVal = 1;
4355  break;
4356  }
4357  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4358  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4359  {
4360  ReturnVal = 2;
4361  break;
4362  }
4363  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4364  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4365  {
4366  ReturnVal = 3;
4367  break;
4368  }
4369  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388,
4370  CurrentTrackVectorPosition).TrackType == Crossover))
4371  {
4372  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4373  // must be a loop - reached same point as examined earlier
4374  {
4375  ReturnVal = 4;
4376  break;
4377  }
4378  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4379  {
4380  ReturnVal = 4;
4381  break;
4382  }
4383  }
4384  else
4385  {
4386  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526,
4387  CurrentTrackVectorPosition).TempTrackMarker23))
4388  {
4389  ReturnVal = 4;
4390  break;
4391  }
4392  }
4393  if(EntryPos < 2)
4394  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4395  else
4396  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4397 
4398  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4399  {
4400  if((EntryPos == 0) || (EntryPos == 2))
4401  {
4402  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4403  ExitPos = 1;
4404  else
4405  ExitPos = 3;
4406  }
4407  else
4408  ExitPos = 0;
4409  }
4410  else
4411  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4412  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4413  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4414  CurrentTrackVectorPosition = NextTrackVectorPosition;
4415  EntryPos = NextEntryPos;
4416  }
4417  if(ReturnVal == 1)
4418  {
4419  Utilities->CallLogPop(708);
4420  return false;
4421  }
4422  if(ReturnVal == 2)
4423  {
4424  Utilities->CallLogPop(709);
4425  return true;
4426  }
4427  if(ReturnVal == 3)
4428  {
4429  Utilities->CallLogPop(946);
4430  return true;
4431  }
4432  if(ReturnVal == 4)
4433  {
4434  Utilities->CallLogPop(947);
4435  return true;
4436  }
4437  else
4438  {
4439  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4440  }
4441 }
4442 
4443 // ---------------------------------------------------------------------------
4444 
4446 /*
4447  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4448  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4449  change of direction (cdt), remaining here (Frh), or under signaller control);
4450  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4451  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4452  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4453  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] and l) no existing route conflicts with the route into the platform
4454  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4455  change points outside the route or have a route conflict if another route is set.
4456 */ {
4457  if(Track->RouteFlashFlag)
4458  return false;
4459 
4460  // don't want to create a new route from the stop signal if one is already in construction as
4461  // some of the callingon route elements may be involved
4462  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4463  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4464  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4465  int RouteStartPosition;
4466  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4467  int PlatformPosition;
4468  // the track vector position of the first stop platfrom
4469  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4470  // not used here
4471  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4472 
4473  // must be stopped at a signal but not at a location & still in timetable (a)
4474 
4476  // no need to check for SignallerStopped as this function only called in Timetable mode
4477  {
4478  Utilities->CallLogPop(711);
4479  return false;
4480  }
4481  while(true)
4482  {
4483  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4484  // don't look further than 4km ahead (j)
4485  if(Distance > 4000)
4486  {
4487  Utilities->CallLogPop(967);
4488  return false;
4489  }
4490  // if find another train on an element in front, before find a valid platform, return false (c)
4491  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4492  {
4493  Utilities->CallLogPop(713);
4494  return false;
4495  }
4496  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4497  // be facing later on)
4498  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4499  {
4500  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4501  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4502  if(OtherTrain.LeadElement == -1)
4503  {
4504  Utilities->CallLogPop(714);
4505  return false;
4506  }
4507  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4508  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4509  {
4510  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4511  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4512  (OtherTrain.TrainMode == Signaller))
4513  {
4514  break;
4515  }
4516  else
4517  {
4518  Utilities->CallLogPop(955);
4519  return false;
4520  }
4521  }
4522  else // (h)
4523  {
4524  break;
4525  }
4526  }
4527  // if reach buffers or exit continuation return false (can set route)
4528  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4529  {
4530  Utilities->CallLogPop(716);
4531  return false;
4532  }
4533  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4534  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4536  {
4537  Utilities->CallLogPop(717);
4538  return false;
4539  }
4540  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4541 /*
4542  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4543  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4544  {
4545  Utilities->CallLogPop(718);
4546  return false;
4547  }
4548 */
4549  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4550  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4551  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4552  {
4553  if(StopRequired)
4554  {
4555  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4556  {
4557  if(!PlatformFoundFlag)
4558  PlatformPosition = CurrentTrackVectorPosition;
4559  // ensure this only set once at first valid platform position - the unrestricted route will end here
4560  PlatformFoundFlag = true;
4561  }
4562  }
4563  }
4564  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4565  // train has to be at station but that has to be before the next forward signal
4566 /*
4567  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4568  {
4569  Utilities->CallLogPop(719);
4570  return false;
4571  }
4572 */
4573  // make sure points are followed correctly (d) & set ExitPos
4574  if(CurrentTrackElement.TrackType == Points)
4575  {
4576  if((EntryPos == 0) || (EntryPos == 2))
4577  {
4578  if(CurrentTrackElement.Attribute == 0)
4579  ExitPos = 1;
4580  else
4581  ExitPos = 3;
4582  }
4583  if(EntryPos == 1)
4584  {
4585  if(CurrentTrackElement.Attribute == 0)
4586  ExitPos = 0;
4587  else
4588  {
4589  Utilities->CallLogPop(720);
4590  return false;
4591  }
4592  }
4593  if(EntryPos == 3)
4594  {
4595  if(CurrentTrackElement.Attribute == 1)
4596  ExitPos = 0;
4597  else
4598  {
4599  Utilities->CallLogPop(721);
4600  return false;
4601  }
4602  }
4603  }
4604  else
4605  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4606 
4607  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4608  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4609  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4610  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4611  if(ElementNumber < 2)
4612  SkipRouteCheck = true;
4613  else
4614  SkipRouteCheck = false;
4615  if(ElementNumber == 1) // the stop signal
4616  {
4617  RouteStartPosition = CurrentTrackVectorPosition;
4618  }
4619 /*
4620  if(ElementNumber == 2)
4621  {
4622  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4623  else AutoSigs = false;
4624  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4625  }
4626 */
4627  if(ElementNumber > 1)
4628  {
4629  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4630  RouteOrPartRouteSet = true;
4631  else
4632  RouteOrPartRouteSet = false;
4633  }
4634  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4635  {
4636  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4637  {
4638  Utilities->CallLogPop(1859);
4639  return false;
4640  }
4641  int ExitLink = CurrentTrackElement.Link[ExitPos];
4642  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4643  {
4644  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4645  {
4646  Utilities->CallLogPop(1850);
4647  return false;
4648  }
4649  }
4650  }
4651 
4652  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
4653  if(EntryPos < 2)
4654  Distance += CurrentTrackElement.Length01;
4655  else
4656  Distance += CurrentTrackElement.Length23;
4657  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
4658  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
4659  CurrentTrackVectorPosition = NextTrackVectorPosition;
4660  EntryPos = NextEntryPos;
4661  ElementNumber++;
4662  } // while(true)
4663 
4664  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
4665  // from the stop signal (note that it may be last in an autosigs route)
4666  // a single element route at the stop signal should have been removed prior to this function being called (that called before
4667  // this in ClockTimer2)
4668 
4669  // now add elements to the CallonVector
4670  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
4671 
4672  AllRoutes->CallonVector.push_back(CallonEntry);
4673  Utilities->CallLogPop(1860);
4674  return true; // return false if fail to set route for any reason
4675 }
4676 
4677 // ---------------------------------------------------------------------------
4678 /*
4679  bool TTrain::TimetableFinished()
4680  {
4681  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
4682  {
4683  return true;
4684  }
4685  return false;
4686  }
4687 */
4688 // ---------------------------------------------------------------------------
4689 
4690 AnsiString TTrain::GetTrainHeadCode(int Caller)
4691 
4692 {
4693  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
4694  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
4695 
4696  Utilities->CallLogPop(1452);
4697  return RepeatHeadCode;
4698 }
4699 
4700 // ---------------------------------------------------------------------------
4701 
4702 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
4703 {
4704  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
4705  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
4706 
4707  Utilities->CallLogPop(1453);
4708  return RepeatTime;
4709 }
4710 
4711 // ---------------------------------------------------------------------------
4712 
4713 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
4714 { // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
4715  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
4716  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
4717  // first check that train is fully on the railway
4718  bool FrontValid = false, RearValid = false;
4719  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
4720 
4721  if((LeadElement == -1) || (MidElement == -1))
4722  {
4723  TrainToBeJoinedBy = NULL;
4724  Utilities->CallLogPop(2131);
4725  return false;
4726  }
4728  {
4729  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
4730  FrontValid = true;
4731  }
4733  {
4734  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
4735  RearValid = true;
4736  }
4737  int TrainToBeJoinedByID = -1;
4738 
4739  // first check if on a 2-track element & select correct ID number if so
4740  if(FrontValid)
4741  {
4742  if(FrontAdjacentTrackElement.TrackType == Bridge)
4743  {
4745  {
4746  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4747  }
4748  else
4749  {
4750  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4751  }
4752  }
4753  else
4754  {
4755  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
4756  }
4757  }
4758  if((TrainToBeJoinedByID < 0) && RearValid)
4759  {
4760  // first check if on a 2-track element & select correct ID number if so
4761  if(RearAdjacentTrackElement.TrackType == Bridge)
4762  {
4764  {
4765  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
4766  }
4767  else
4768  {
4769  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
4770  }
4771  }
4772  else
4773  {
4774  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
4775  }
4776  }
4777  if(TrainToBeJoinedByID < 0) // no adjacent train
4778  {
4779  TrainToBeJoinedBy = NULL;
4780  Utilities->CallLogPop(2132);
4781  return false;
4782  }
4783  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
4784  if(!TrainToBeJoinedBy->Stopped())
4785  {
4786  TrainToBeJoinedBy = NULL;
4787  Utilities->CallLogPop(2133);
4788  return false;
4789  }
4790  Utilities->CallLogPop(2134);
4791  return true;
4792 }
4793 
4794 // ---------------------------------------------------------------------------
4795 
4796 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
4797  TDateTime TimetableNonRepeatTime, bool Warning)
4798 /*
4799  Time = timetable time, the time adjustments for repeat trains is carried out internally
4800  Not all messages need this, if not needed a dummy value is required but not used
4801 
4802  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
4803  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
4804  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
4805  //NB for Frh just give terminated message but without event time - don't use this function
4806  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
4807  Create: 06:05:40: 2F46 created at Old Street 1 minute late
4808  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
4809  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
4810  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
4811  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
4812  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
4813  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
4814  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
4815  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
4816  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
4817  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
4818  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
4819  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
4820  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
4821  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
4822  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
4823  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
4824  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
4825  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
4826  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
4827  SignallerStop 06:05:40: 2F46 stopped on signaller command
4828  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
4829  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
4830 */ {
4831  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
4832  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
4833  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
4834  int IntMinsLate = 0;
4835 
4836  // need to set it in case MinsLate == 0, since it isn't tested for that
4837  if(ActionType == Arrive)
4838  ActionLog = " arrived at ";
4839  if(ActionType == Terminate)
4840  {
4841  if(TerminatedMessageSent) // to avoid it being sent twice
4842  {
4843  Utilities->CallLogPop(1104);
4844  return;
4845  }
4846  ActionLog = " terminated at ";
4847  TerminatedMessageSent = true;
4848  }
4849  if(ActionType == Depart)
4850  ActionLog = " departed from ";
4851  if(ActionType == Pass)
4852  ActionLog = " passed ";
4853  if(ActionType == Create)
4854  ActionLog = " created at ";
4855  if(ActionType == Enter)
4856  ActionLog = " entered railway at ";
4857  if(ActionType == Leave)
4858  ActionLog = " left railway at ";
4859  if(ActionType == FrontSplit)
4860  ActionLog = " split from front to ";
4861  if(ActionType == RearSplit)
4862  ActionLog = " split from rear to ";
4863  if(ActionType == JoinedByOther)
4864  ActionLog = " joined by ";
4865  if(ActionType == ChangeDirection)
4866  ActionLog = " changed direction at ";
4867  if(ActionType == NewService)
4868  ActionLog = " became new service ";
4869  if(ActionType == TakeSignallerControl)
4870  ActionLog = " taken under signaller control at ";
4871  if(ActionType == RestoreTimetableControl)
4872  ActionLog = " restored to timetable control at ";
4873  if(ActionType == RemoveTrain)
4874  {
4875  if(Crashed)
4876  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
4877  else if(Derailed)
4878  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
4879  else
4880  ActionLog = " REMOVED FROM RAILWAY at ";
4881  }
4882  if(ActionType == SignallerMoveForwards)
4883  ActionLog = " received signaller authority to proceed";
4884  if(ActionType == SignallerStepForward)
4885  ActionLog = " received signaller authority to step forward";
4886  if(ActionType == SignallerChangeDirection)
4887  ActionLog = " changed direction under signaller control at ";
4888  if(ActionType == SignallerPassRedSignal)
4889  ActionLog = " received signaller authority to pass red signal";
4890  if(ActionType == SignallerControlStop)
4891  ActionLog = " received signaller instruction to stop";
4892  if(ActionType == SignallerStop)
4893  ActionLog = " stopped on signaller instruction ";
4894  if(ActionType == SignallerJoin)
4895  ActionLog = " joined under signaller control by ";
4896  if(ActionType == TrainFailure)
4897  ActionLog = " suffered an onboard power failure at ";
4898  if(ActionType == RepairFailedTrain)
4899  ActionLog = " failure repaired at ";
4900  if(ActionType == SignallerLeave)
4901  ActionLog = " left railway under signaller control at ";
4902  if(OtherHeadCode != "")
4903  OtherHeadCode += " at ";
4904  TDateTime ActualTime = TrainController->TTClockTime;
4905 
4906  if(Warning)
4907  {
4908  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
4909  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
4910  }
4911  else
4912  {
4913  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
4914  }
4915  bool TimePerformance = true;
4916 
4917  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
4918  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
4919  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
4920  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
4921  // SignallerJoin & RepairFailedTrain new at v2.4.0
4922  {
4923  TimePerformance = false;
4924  }
4925  if(TimePerformance)
4926  {
4927  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
4928  MinsDelayed = float(MinsLate);
4929  // new v2.2.0 for OpActionPanel, can be positive or negative
4930  if(ActionType == Arrive)
4932  // since train has just arrived this value is the
4933  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
4934  // subtracted from later stop recoverable times.
4935  if(MinsLate < 0)
4936  {
4937  IntMinsLate = int(ceil(MinsLate));
4938  }
4939  if(MinsLate > 0)
4940  {
4941  IntMinsLate = int(floor(MinsLate));
4942  }
4943  if(IntMinsLate == 0)
4944  {
4945  PerfLog = " on time";
4946  }
4947  else if(IntMinsLate == 1)
4948  PerfLog = " 1 minute late";
4949  else if(IntMinsLate == -1)
4950  PerfLog = " 1 minute early";
4951  else if(IntMinsLate > 1)
4952  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
4953  else if(IntMinsLate < -1)
4954  {
4955  int PosIntMinsLate = -IntMinsLate;
4956  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
4957  }
4958  if(LocationName.Pos('-') > 0)
4959  {
4960  PerfLog = "," + PerfLog;
4961  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
4962  }
4963  Display->PerformanceLog(0, BaseLog + PerfLog);
4964  }
4965  else
4966  Display->PerformanceLog(1, BaseLog);
4967  if(Warning)
4968  {
4969  Display->WarningLog(0, WarningBaseLog);
4970  }
4971 
4972  // update statistics
4973  if((ActionType == Arrive) && (IntMinsLate == 0))
4974  {
4976  }
4977  else if((ActionType == Arrive) && (IntMinsLate > 0))
4978  {
4980  TrainController->TotLateArrMins += IntMinsLate;
4981  }
4982  else if((ActionType == Arrive) && (IntMinsLate < 0))
4983  {
4985  TrainController->TotEarlyArrMins += abs(IntMinsLate);
4986  }
4987  else if((ActionType == Pass) && (IntMinsLate == 0))
4988  {
4990  }
4991  else if((ActionType == Pass) && (IntMinsLate > 0))
4992  {
4994  TrainController->TotLatePassMins += IntMinsLate;
4995  }
4996  else if((ActionType == Pass) && (IntMinsLate < 0))
4997  {
4999  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5000  }
5001  else if((ActionType == Depart) && (IntMinsLate == 0))
5002  {
5004  }
5005  else if((ActionType == Depart) && (IntMinsLate > 0))
5006  {
5008  TrainController->TotLateDepMins += IntMinsLate;
5009  }
5010  Utilities->CallLogPop(968);
5011 }
5012 
5013 // ---------------------------------------------------------------------------
5014 
5015 void TTrain::TrainHasFailed(int Caller)
5016 {
5017  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5018  if(Crashed || Derailed || DerailPending)
5019  {
5020  TrainFailurePending = false;
5021  Utilities->CallLogPop(2135);
5022  return;
5023  }
5024  AnsiString LocName = "";
5025 
5026  if(LeadElement > -1)
5027  {
5029  }
5030  if((LocName == "") && (MidElement > -1))
5031  {
5033  }
5034  if((LocName == "") && LeadElement > -1)
5035  {
5036  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5037  }
5038  if((LocName == "") && (MidElement > -1))
5039  {
5040  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5041  }
5042  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5043  TrainFailed = true;
5044  TrainFailurePending = false;
5046  PowerAtRail = 0.08;
5047  AValue = sqrt(2 * PowerAtRail / Mass);
5049  // TrainFailed only called when PlotElements properly set to lead, Mid & Lag elements
5050  if(Stopped())
5051  {
5052  EntrySpeed = 0;
5053  ExitSpeedHalf = 0;
5054  ExitSpeedFull = 0;
5055  MaxExitSpeed = 0;
5056  BrakeRate = 0;
5057  StoppedWithoutPower = true;
5058  }
5060  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5061  // true for warning, TDateTime not used
5062  Utilities->CallLogPop(2136);
5063 }
5064 
5065 // ---------------------------------------------------------------------------
5066 
5067 void TTrain::FrontTrainSplit(int Caller)
5068 {
5069 /*
5070  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5071  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5072  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5073  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5074 */
5075  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5076  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5077  if(PowerAtRail < 1)
5078  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5079  {
5081  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5083  Utilities->CallLogPop(2137);
5084  return;
5085  }
5086  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5087 
5088  if(LocationName == "")
5089  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5090  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5091  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5092  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5094 
5095  // determine all positions & exits
5096  if(LocationName != "")
5097  {
5098  // if message given only call at ~5 sec intervals
5100  {
5101  FirstNamedElementPos = LeadElement;
5102  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5103  // check if possible with LeadElement as First
5104  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5105  {
5106  FirstNamedElementPos = MidElement;
5107  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5108  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5109  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5110  {
5112  {
5113  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5115  }
5116  Utilities->CallLogPop(1009);
5117  return;
5118  }
5119  }
5120  else
5121  {
5122  // if first is possible then check if all 4 positions at location, and if not try the second
5123  int LeadPosA = FirstNamedElementPos;
5124  int LeadPosB = FirstNamedLinkedElementPos;
5125  int LeadPosC = SecondNamedElementPos;
5126  int LeadPosD = SecondNamedLinkedElementPos;
5127  // count number of positions that are at the location
5128  int LeadNumAtLoc = 0;
5129  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5130  LeadNumAtLoc++;
5131  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5132  LeadNumAtLoc++;
5133  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5134  LeadNumAtLoc++;
5135  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5136  LeadNumAtLoc++;
5137  if(LeadNumAtLoc < 4)
5138  {
5139  FirstNamedElementPos = MidElement;
5140  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5141  SecondNamedLinkedElementPos)) // restore originals
5142  {
5143  FirstNamedElementPos = LeadPosA;
5144  FirstNamedLinkedElementPos = LeadPosB;
5145  SecondNamedElementPos = LeadPosC;
5146  SecondNamedLinkedElementPos = LeadPosD;
5147  }
5148  else // count number at location
5149  {
5150  int MidNumAtLoc = 0;
5151  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5152  MidNumAtLoc++;
5153  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5154  MidNumAtLoc++;
5155  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5156  MidNumAtLoc++;
5157  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5158  MidNumAtLoc++;
5159  if(LeadNumAtLoc > MidNumAtLoc)
5160  // change back, else keep new values
5161  {
5162  FirstNamedElementPos = LeadPosA;
5163  FirstNamedLinkedElementPos = LeadPosB;
5164  SecondNamedElementPos = LeadPosC;
5165  SecondNamedLinkedElementPos = LeadPosD;
5166  }
5167  }
5168  }
5169  }
5170  }
5171  else
5172  {
5173  Utilities->CallLogPop(1791);
5174  return;
5175  }
5176  }
5177  else
5178  throw Exception("Error - LocationName not set in FrontTrainSplit");
5179  // set front & rear train parameters
5180  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5181  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5182  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5183  if(LeadElement == FirstNamedElementPos)
5184  {
5185  if(MidElement == SecondNamedElementPos)
5186  {
5187  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5188  FrontTrainRearPosition = FirstNamedElementPos;
5189  RearTrainFrontPosition = SecondNamedElementPos;
5190  RearTrainRearPosition = SecondNamedLinkedElementPos;
5191  }
5192  else // MidElement must == FirstNamedLinkedElementPos
5193  {
5194  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5195  FrontTrainRearPosition = SecondNamedElementPos;
5196  RearTrainFrontPosition = FirstNamedElementPos;
5197  RearTrainRearPosition = FirstNamedLinkedElementPos;
5198  }
5199  }
5200  else // MidElement == FirstNamedElementPos
5201  {
5202  if(LeadElement == SecondNamedElementPos)
5203  {
5204  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5205  FrontTrainRearPosition = SecondNamedElementPos;
5206  RearTrainFrontPosition = FirstNamedElementPos;
5207  RearTrainRearPosition = FirstNamedLinkedElementPos;
5208  }
5209  else // LeadElement must == FirstNamedLinkedElementPos
5210  {
5211  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5212  FrontTrainRearPosition = FirstNamedElementPos;
5213  RearTrainFrontPosition = SecondNamedElementPos;
5214  RearTrainRearPosition = SecondNamedLinkedElementPos;
5215  }
5216  }
5217  RearTrainExitPos = -1;
5218  for(int x = 0; x < 4; x++)
5219  {
5220  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5221  {
5222  RearTrainExitPos = x;
5223  break;
5224  }
5225  }
5226  if(RearTrainExitPos == -1)
5227  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5228 
5229  FrontTrainExitPos = -1;
5230  for(int x = 0; x < 4; x++)
5231  {
5232  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5233  {
5234  FrontTrainExitPos = x;
5235  break;
5236  }
5237  }
5238  if(FrontTrainExitPos == -1)
5239  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5240 
5241  // check no train (apart from self) on any of the 4 elements & fail if so
5242  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5243  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5244 
5245  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5246  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5247  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5248  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5249  else
5250  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5251  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5252  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5253  // can't be a bridge
5254  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5255  // can't be a bridge
5256  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5257  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5258 
5259  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5260  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5261  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5262  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5263  else
5264  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5265 
5266  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5267  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5268  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5269  {
5271  {
5274  }
5275  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5276  Utilities->CallLogPop(1010);
5277  return;
5278  }
5279 
5281  {
5283  }
5284  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5285  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5286  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5287  // variable as it is needed for setting up the new train
5288  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5289 
5290  UnplotTrain(0);
5291  StartSpeed = 0;
5292  RearStartElement = RearTrainRearPosition;
5293  RearStartExitPos = RearTrainExitPos;
5294  StoppedAtLocation = true;
5295  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5296  {
5297  StoppedWithoutPower = true;
5298  }
5299  PlotStartPosition(3);
5304 
5305  Mass = Mass / 2;
5306  MaxBrakeRate = MaxBrakeRate / 2;
5307  PowerAtRail = PowerAtRail / 2;
5308  AValue = sqrt(2 * PowerAtRail / Mass);
5309  // shouldn't change but include in case not set earlier
5310 
5311  // create new front train
5312 /*
5313  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5314  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5315  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5316 */
5317  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5318  TActionEventType EventType = NoEvent;
5319 
5320  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5321  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5322  // false for SignallerControl
5323  {
5324  Utilities->CallLogPop(1721); // EventType not used here
5325  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5326  // another train, in which case a message will have been sent to the perf log, also might well clear later
5327  // when other train moves away
5328  return;
5329  }
5330  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5331  // see mods in UpdateTrain for v1.3.2
5332  TrainController->TrainAdded = true;
5333 
5334  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5335 
5336  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5337  TTOD.RunningEntry = Running;
5338  Utilities->CallLogPop(998);
5339 }
5340 
5341 // ---------------------------------------------------------------------------
5342 
5343 void TTrain::RearTrainSplit(int Caller)
5344 {
5345 /*
5346  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5347  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5348  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5349  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5350 */
5351  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5352  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5353  if(PowerAtRail < 1)
5354  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5355  {
5357  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5359  Utilities->CallLogPop(2138);
5360  return;
5361  }
5362  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5363 
5364  if(LocationName == "")
5365  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5366  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5367  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5368  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5370 
5371  // determine all positions & exits
5372  if(LocationName != "")
5373  {
5374  // if message given only call at ~5 sec intervals
5376  {
5377  FirstNamedElementPos = LeadElement;
5378  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5379  SecondNamedLinkedElementPos))
5380  {
5381  FirstNamedElementPos = MidElement;
5382  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5383  SecondNamedLinkedElementPos))
5384  {
5386  {
5387  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5389  }
5390  Utilities->CallLogPop(1013);
5391  return;
5392  }
5393  }
5394  else
5395  {
5396  // if first is possible then check if all 4 positions at location, and if not try the second
5397  int LeadPosA = FirstNamedElementPos;
5398  int LeadPosB = FirstNamedLinkedElementPos;
5399  int LeadPosC = SecondNamedElementPos;
5400  int LeadPosD = SecondNamedLinkedElementPos;
5401  // count number of positions that are at the location
5402  int LeadNumAtLoc = 0;
5403  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5404  LeadNumAtLoc++;
5405  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5406  LeadNumAtLoc++;
5407  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5408  LeadNumAtLoc++;
5409  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5410  LeadNumAtLoc++;
5411  if(LeadNumAtLoc < 4)
5412  {
5413  FirstNamedElementPos = MidElement;
5414  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5415  SecondNamedLinkedElementPos)) // restore originals
5416  {
5417  FirstNamedElementPos = LeadPosA;
5418  FirstNamedLinkedElementPos = LeadPosB;
5419  SecondNamedElementPos = LeadPosC;
5420  SecondNamedLinkedElementPos = LeadPosD;
5421  }
5422  else // count number at location
5423  {
5424  int MidNumAtLoc = 0;
5425  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5426  MidNumAtLoc++;
5427  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5428  MidNumAtLoc++;
5429  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5430  MidNumAtLoc++;
5431  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5432  MidNumAtLoc++;
5433  if(LeadNumAtLoc > MidNumAtLoc)
5434  // change back, else keep new values
5435  {
5436  FirstNamedElementPos = LeadPosA;
5437  FirstNamedLinkedElementPos = LeadPosB;
5438  SecondNamedElementPos = LeadPosC;
5439  SecondNamedLinkedElementPos = LeadPosD;
5440  }
5441  }
5442  }
5443  }
5444  }
5445  else
5446  {
5447  Utilities->CallLogPop(1792);
5448  return;
5449  }
5450  }
5451  else
5452  throw Exception("Error - LocationName not set in RearTrainSplit");
5453  // set front & rear train parameters
5454  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5455  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5456  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5457  if(LeadElement == FirstNamedElementPos)
5458  {
5459  if(MidElement == SecondNamedElementPos)
5460  {
5461  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5462  FrontTrainRearPosition = FirstNamedElementPos;
5463  RearTrainFrontPosition = SecondNamedElementPos;
5464  RearTrainRearPosition = SecondNamedLinkedElementPos;
5465  }
5466  else // MidElement must == FirstNamedLinkedElementPos
5467  {
5468  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5469  FrontTrainRearPosition = SecondNamedElementPos;
5470  RearTrainFrontPosition = FirstNamedElementPos;
5471  RearTrainRearPosition = FirstNamedLinkedElementPos;
5472  }
5473  }
5474  else // MidElement == FirstNamedElementPos
5475  {
5476  if(LeadElement == SecondNamedElementPos)
5477  {
5478  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5479  FrontTrainRearPosition = SecondNamedElementPos;
5480  RearTrainFrontPosition = FirstNamedElementPos;
5481  RearTrainRearPosition = FirstNamedLinkedElementPos;
5482  }
5483  else // LeadElement must == FirstNamedLinkedElementPos
5484  {
5485  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5486  FrontTrainRearPosition = FirstNamedElementPos;
5487  RearTrainFrontPosition = SecondNamedElementPos;
5488  RearTrainRearPosition = SecondNamedLinkedElementPos;
5489  }
5490  }
5491  RearTrainExitPos = -1;
5492  for(int x = 0; x < 4; x++)
5493  {
5494  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5495  {
5496  RearTrainExitPos = x;
5497  break;
5498  }
5499  }
5500  if(RearTrainExitPos == -1)
5501  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5502  FrontTrainExitPos = -1;
5503  for(int x = 0; x < 4; x++)
5504  {
5505  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5506  {
5507  FrontTrainExitPos = x;
5508  break;
5509  }
5510  }
5511  if(FrontTrainExitPos == -1)
5512  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
5513 
5514  // check no train (apart from self) on any of the 4 elements & fail if so
5515  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5516  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
5517 
5518  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5519  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5520  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5521  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5522  else
5523  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5524  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5525  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
5526  // can't be a bridge
5527  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
5528  // can't be a bridge
5529  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5530  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
5531 
5532  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5533  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5534  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5535  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5536  else
5537  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5538 
5539  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5540  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5541  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5542  {
5544  {
5547  }
5548  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5549  Utilities->CallLogPop(1014);
5550  return;
5551  }
5552 
5554  {
5556  }
5557 
5558  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
5559  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5560  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5561  // variable as it is needed for setting up the new train
5562  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5563 
5564  UnplotTrain(1);
5565  StartSpeed = 0;
5566  RearStartElement = FrontTrainRearPosition;
5567  RearStartExitPos = FrontTrainExitPos;
5568  StoppedAtLocation = true;
5569  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5570  {
5571  StoppedWithoutPower = true;
5572  }
5573  PlotStartPosition(4);
5578  Mass = Mass / 2;
5579  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5580  MaxBrakeRate = MaxBrakeRate / 2;
5581  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5582  PowerAtRail = PowerAtRail / 2;
5583  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5584  AValue = sqrt(2 * PowerAtRail / Mass);
5585  // shouldn't change but include in case not set earlier
5586 
5587  // create new rear train
5588 /*
5589  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5590  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5591  int RepeatNumber, int IncrementalMinutes)
5592 */
5593  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5594  TActionEventType EventType = NoEvent;
5595 
5596  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5597  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5598  // false for SignallerControl
5599  {
5600  Utilities->CallLogPop(1722); // EventType not used here
5601  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5602  // another train, in which case a message will have been sent to the perf log, also might well clear later
5603  // when other train moves away
5604  return;
5605  }
5606  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5607  // see mods in UpdateTrain for v1.3.2
5608  TrainController->TrainAdded = true;
5609  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5610 
5611  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5612  TTOD.RunningEntry = Running;
5613  Utilities->CallLogPop(1015);
5614 }
5615 
5616 // ---------------------------------------------------------------------------
5617 
5618 void TTrain::FinishJoin(int Caller)
5619 {
5620  if(FinishJoinLogSent == false)
5621  {
5622  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5623  FinishJoinLogSent = true; // so don't keep logging this event
5624  // don't need to reset it to false after the event as the train is deleted
5625  }
5626  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
5627  if(TrainFailed)
5628  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
5629  {
5631  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
5633  Utilities->CallLogPop(2139);
5634  return;
5635  }
5636  if(TrainGone)
5637  // this means that the train has already joined the other & is awaiting deletion by TrainController
5638  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
5639  // on from jbo & TrainToJoinIsAdjacent returns false
5640  {
5641  Utilities->CallLogPop(1035);
5642  return;
5643  }
5644  TTrain *TrainToJoin;
5646 
5647  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
5648  {
5650  {
5651  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
5654  }
5655  Utilities->CallLogPop(1030);
5656  return; // keep this here in case need to add code before final return
5657  }
5658  // no need to clear error report flag here, cleared in jbo function
5659  // No need to set TimetableFinished, done in jbo function
5660  Utilities->CallLogPop(1031);
5661 }
5662 
5663 // ---------------------------------------------------------------------------
5664 
5665 void TTrain::JoinedBy(int Caller)
5666 {
5667  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
5668  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
5669  if(PowerAtRail < 1)
5670  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
5671  {
5673  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
5675  Utilities->CallLogPop(2140);
5676  return;
5677  }
5678  TTrain *TrainToBeJoinedBy;
5680 
5681  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
5682  {
5684  {
5685  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
5688  }
5689  LastActionDelayFlag = true;
5690  // need to update LastActionTime if this train first to arrive as need 30s after
5691  // both adjacent before the join
5692  Utilities->CallLogPop(1032);
5693  return;
5694  }
5695  // here when other train is adjacent
5697  {
5699  // need to update this as need 30s after both adjacent before the join
5700  LastActionDelayFlag = false;
5701  Utilities->CallLogPop(1033);
5702  return;
5703  }
5704  // here when other train is adjacent & 30 secs elapsed since both adjacent
5705 
5706  // set new values for mass etc
5707  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
5708  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
5709  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
5710  double OwnBrakeForce = MaxBrakeRate * Mass;
5711  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
5712 
5713  Mass += TrainToBeJoinedBy->Mass;
5714  MaxBrakeRate = CombinedBrakeRate;
5715  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
5716  AValue = sqrt(2 * PowerAtRail / Mass);
5717 
5719  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
5720  TrainToBeJoinedBy->TimetableFinished = true;
5721  TrainToBeJoinedBy->TrainGone = true;
5722  // this will cause other train to be deleted
5723  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
5727  Utilities->CallLogPop(1034);
5728 }
5729 
5730 // ---------------------------------------------------------------------------
5731 
5733 {
5734  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
5735  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
5736  if(PowerAtRail < 1)
5737  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
5738  {
5740  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
5741  ZeroPowerNoCDTMessage = true;
5742  Utilities->CallLogPop(2141);
5743  return;
5744  }
5745  TColor TempColour = BackgroundColour;
5746 
5747  UnplotTrain(2);
5750  StartSpeed = 0;
5751  StoppedAtLocation = true;
5752  PlotStartPosition(1);
5753  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
5754  // plot same as was - should always be pale green
5758 
5759  //now erase a stub route if there is one, added at v2.5.1
5760  //first element of route is now immediately behind the train (i.e. next to MidElement)
5761  if(MidEntryPos >= 0)
5762  {
5763  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
5764  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
5765  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
5766  int RouteNumber = -1;
5767  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
5768  if(RouteType == TAllRoutes::NotAutoSigsRoute)
5769  {
5770  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
5771  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
5772  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))//all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
5773  {
5774  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
5775  {
5776  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
5777  int TVPos2 = PDE.GetTrackVectorPosition();
5778  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
5780  {
5781  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
5782  }
5783  else
5784  {
5785  break;
5786  }
5787  }
5788  AllRoutes->RebuildRailwayFlag = true;
5789  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
5790  }
5791  }
5792  }
5793  Utilities->CallLogPop(1012);
5794 }
5795 
5796 // ---------------------------------------------------------------------------
5797 
5798 void TTrain::NewTrainService(int Caller)
5799  // change to new train, give new service message
5800 {
5801  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
5802  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
5803  if(PowerAtRail < 1)
5804  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
5805  {
5807  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
5809  Utilities->CallLogPop(2142);
5810  return;
5811  }
5813 
5815  UnplotTrain(3);
5818  StartSpeed = 0;
5823  HeadCode = NewHeadCode;
5824  StoppedAtLocation = true;
5825  PlotStartPosition(5);
5827  // pale green
5830  TerminatedMessageSent = false;
5831  Utilities->CallLogPop(1022);
5832 }
5833 
5834 // ---------------------------------------------------------------------------
5835 
5836 void TTrain::RemainHere(int Caller)
5837 {
5838  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
5839  if(RemainHereLogNotSent) // to prevent repeated logs
5840  {
5841  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
5842  RemainHereLogNotSent = false;
5843  }
5845  {
5846  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
5849  TerminatedMessageSent = true;
5850  }
5851  TimetableFinished = true;
5852  Utilities->CallLogPop(1023);
5853 }
5854 
5855 // ---------------------------------------------------------------------------
5856 
5857 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
5858 /*
5859  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
5860  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
5861  except where an action is a departure, starting at the current value for the pointer
5862  If IncNum is -1, then send messages for all remaining actions, including Fer if present
5863  If IncNum is -2, then send messages for all remaining actions, except Fer if present
5864 */ {
5865  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
5866  return; // if remove train that starts under signaller control no messages needed
5867 
5868  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
5869  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
5870  if(IncNum > 0)
5871  {
5872  for(int x = 0; x < IncNum; x++)
5873  {
5874  if(x > 0)
5875  Ptr++;
5876  // arrival - no need to test for termination as this section only covers missed actions up to the
5877  // arrival point - may terminate later but that not missed
5878  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
5879  {
5881  }
5882  // arrival & departure
5883  if(Ptr->FormatType == TimeTimeLoc)
5884  {
5886  }
5887  // departure
5888  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
5889  {
5890  continue; // skip TimeLoc departures, message given for arrivals
5891  }
5892  // pass
5893  else if(Ptr->FormatType == PassTime)
5894  {
5896  }
5897  // split
5898  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
5899  {
5901  }
5902  // jbo
5903  else if(Ptr->Command == "jbo")
5904  {
5906  }
5907  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
5908  // be starts, finishes or cdt
5909  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
5910  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
5911  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
5912  (Ptr->FormatType == Repeat))
5913  {
5914  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
5915  }
5916  }
5917  }
5918  else
5919  {
5920  bool IncludeFER = false;
5921  if(IncNum == -1)
5922  IncludeFER = true;
5923  while(true) // finish commands & repeats break out of loop
5924  {
5925  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
5926  if(!IncludeFER && (Ptr->Command == "Fer"))
5927  {
5928  break;
5929  }
5930  // Fer & included
5931  else if(IncludeFER && (Ptr->Command == "Fer"))
5932  {
5934  break;
5935  }
5936  // Repeat
5937  else if(Ptr->FormatType == Repeat)
5938  {
5939  break;
5940  }
5941  // Fjo
5942  else if(Ptr->Command == "Fjo")
5943  {
5945  break;
5946  }
5947  // Frh
5948  else if(Ptr->Command == "Frh")
5949  {
5951  {
5953  TerminatedMessageSent = true;
5954  }
5955  break;
5956  }
5957  // Frh-sh
5958  else if(Ptr->Command == "Frh-sh")
5959  {
5961  {
5963  TerminatedMessageSent = true;
5964  }
5965  break;
5966  }
5967  // Fns, F-nshs, Fns-sh
5968  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
5969  {
5971  break;
5972  }
5973  // end of breakout actions
5974  // arrival
5975  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
5976  {
5977  if(IsTrainTerminating(1))
5978  {
5980  TerminatedMessageSent = true;
5981  }
5982  else
5983  {
5985  }
5986  }
5987  // arrival & departure
5988  else if(Ptr->FormatType == TimeTimeLoc)
5989  {
5991  }
5992  // departure
5993  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
5994  {
5995  Ptr++;
5996  continue; // skip TimeLoc departures, message given for arrivals
5997  }
5998  // pass
5999  else if(Ptr->FormatType == PassTime)
6000  {
6002  }
6003  // split
6004  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6005  {
6007  }
6008  // jbo
6009  else if(Ptr->Command == "jbo")
6010  {
6012  }
6013  // cdt
6014  else if(Ptr->Command == "cdt")
6015  {
6017  }
6018  // Errors
6019  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6020  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6021  {
6022  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6023  }
6024  Ptr++;
6025  }
6026  TimetableFinished = true;
6027  }
6028  Utilities->CallLogPop(1021);
6029 }
6030 
6031 // ---------------------------------------------------------------------------
6032 
6033 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6034  // ensure same repeatnumber
6035 {
6036  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6038 
6039  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6040  {
6041  Utilities->CallLogPop(1024);
6042  return false;
6043  }
6044  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6045  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6046  {
6047  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6048  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6049  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6050  {
6051  Utilities->CallLogPop(1025);
6052  return true;
6053  }
6054  }
6055  Utilities->CallLogPop(1026);
6056  return false;
6057 }
6058 
6059 // ---------------------------------------------------------------------------
6060 
6061 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6062  // ensure same repeatnumber
6063 {
6064  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6065  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6066 
6067  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6068  {
6069  Utilities->CallLogPop(1027);
6070  return false;
6071  }
6072  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6073  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6074  {
6075  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6076  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6077  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6078  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6079  {
6080  Utilities->CallLogPop(1028);
6081  return true;
6082  }
6083  }
6084  Utilities->CallLogPop(1029);
6085  return false;
6086 }
6087 
6088 // ---------------------------------------------------------------------------
6089 
6091 {
6092  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6093  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6094  if(PowerAtRail < 1)
6095  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6096  {
6098  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6100  Utilities->CallLogPop(2143);
6101  return;
6102  }
6103  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6104 
6106  UnplotTrain(4);
6109  StartSpeed = 0;
6114  HeadCode = NewHeadCode;
6115  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6116  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6117  StoppedAtLocation = true;
6118  PlotStartPosition(6);
6120  // pale green
6123  TerminatedMessageSent = false;
6124  Utilities->CallLogPop(1078);
6125 }
6126 
6127 // ---------------------------------------------------------------------------
6128 
6130  // need to check whether all repeats finished or not
6131 {
6132  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6133  if(RemainHereLogNotSent) // to prevent repeated logs
6134  {
6135  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6136  RemainHereLogNotSent = false;
6137  }
6139  // finished all repeats
6140  {
6142  {
6145  TerminatedMessageSent = true;
6146  // no need to clear message as no more actions
6147  }
6148  TimetableFinished = true;
6149  Utilities->CallLogPop(1080);
6150  return;
6151  }
6152  if(PowerAtRail < 1)
6153  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6154  {
6156  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6158  Utilities->CallLogPop(2144);
6159  return;
6160  }
6161  int TempRepeatNumber = RepeatNumber + 1;
6162  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6163  // until after LogAction or the wrong time will be used
6164  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6165 
6167  RepeatNumber++;
6168  UnplotTrain(5);
6171  StartSpeed = 0;
6176  HeadCode = NewHeadCode;
6177  StoppedAtLocation = true;
6178  PlotStartPosition(7);
6180  // pale green
6183  TerminatedMessageSent = false;
6184  Utilities->CallLogPop(1079);
6185 }
6186 
6187 // ---------------------------------------------------------------------------
6188 
6190 {
6191  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6192  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6193  if(PowerAtRail < 1)
6194  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6195  {
6197  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6199  Utilities->CallLogPop(2145);
6200  return;
6201  }
6203  // finished all repeats
6204  {
6205  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6207  RepeatNumber = 0;
6208  UnplotTrain(6);
6211  StartSpeed = 0;
6213  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6214  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6216  HeadCode = NewHeadCode;
6217  StoppedAtLocation = true;
6218  PlotStartPosition(9);
6222  TerminatedMessageSent = false;
6223  Utilities->CallLogPop(1081);
6224  return;
6225  }
6226  int TempRepeatNumber = RepeatNumber + 1;
6227  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6228  // until after LogAction or the wrong time will be used
6229  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6230 
6232  RepeatNumber++;
6233  UnplotTrain(7);
6236  StartSpeed = 0;
6241  HeadCode = NewHeadCode;
6242  StoppedAtLocation = true;
6243  PlotStartPosition(8);
6245  // pale green
6248  TerminatedMessageSent = false;
6249  Utilities->CallLogPop(1082);
6250 }
6251 
6252 // ---------------------------------------------------------------------------
6253 
6255 {
6256  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6257  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6258  // must be preceded by a TimeLoc departure
6259  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6260  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6261  {
6263  {
6264  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6265  {
6266  Utilities->CallLogPop(1083);
6267  return false;
6268  }
6269  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6270  {
6271  Utilities->CallLogPop(1084);
6272  return true;
6273  }
6274  }
6275  }
6276  Utilities->CallLogPop(1085);
6277  return false;
6278 }
6279 
6280 // ---------------------------------------------------------------------------
6281 
6282 bool TTrain::AbleToMove(int Caller)
6283 {
6284  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6285  bool Able = true;
6286 
6287  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0 &
6288  { // StoppedForTrainInFront removed as tested below
6289  Able = false;
6290  Utilities->CallLogPop(2146); // added v2.4.0
6291  return Able; // added v2.4.0
6292  }
6293  if(LeadElement > -1)
6294  {
6295  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6296  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6297  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront)
6298  {
6299  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6300  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6301  {
6302  Able = true;
6303  StoppedForTrainInFront = false;
6304  }
6305  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6306  {
6307  Able = true;
6308  StoppedForTrainInFront = false;
6309  }
6310  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6311  {
6312  Able = true;
6313  StoppedForTrainInFront = false;
6314  }
6315  }
6316  else
6317  {
6319  Able = false;
6320  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6321  }
6322  }
6323  else // leaving at a continuation so keep going
6324  {
6325  Able = true;
6326  StoppedForTrainInFront = false;
6327  }
6328  Utilities->CallLogPop(1454);
6329  return Able;
6330 }
6331 
6332 // ---------------------------------------------------------------------------
6333 
6335 {
6336  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6337  // won't be set; if there is a train then set StoppedForTrainInFront
6338  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6339  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6340  if(LeadElement == -1) // exiting at continuation
6341  {
6342  Utilities->CallLogPop(2045);
6343  return false;
6344  }
6345  // end of addition
6346  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6347  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6348 
6349  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6350  {
6351  StoppedForTrainInFront = true;
6352  Utilities->CallLogPop(1455);
6353  return false;
6354  }
6355  else
6356  {
6357  Utilities->CallLogPop(1456);
6359  // StoppedWithoutPower added v2.4.0
6360  }
6361 }
6362 
6363 // ---------------------------------------------------------------------------
6364 
6366 {
6367  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6368  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6369  TColor TempColour = BackgroundColour;
6370 
6371  UnplotTrain(8);
6374  StartSpeed = 0;
6375  PlotStartPosition(2);
6376  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6377 
6378  //now erase a stub route if there is one, added at v2.5.1
6379  //first element of route is now immediately behind the train (i.e. next to MidElement)
6380  if(MidEntryPos >= 0)
6381  {
6382  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6383  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6384  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6385  int RouteNumber = -1;
6386  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6387  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6388  {
6389  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6390  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6391  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation))//all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6392  {
6393  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6394  {
6395  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6396  int TVPos2 = PDE.GetTrackVectorPosition();
6397  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6399  {
6400  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6401  }
6402  else
6403  {
6404  break;
6405  }
6406  }
6407  AllRoutes->RebuildRailwayFlag = true;
6408  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6409  }
6410  }
6411  }
6412  Utilities->CallLogPop(1102);
6413 }
6414 
6415 // ---------------------------------------------------------------------------
6416 
6418 {
6419  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6420  ",FloatingLabelNextString" + "," + HeadCode);
6421  AnsiString RetStr = "", LocationName = "";
6422 
6423  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6424  {
6425  throw Exception("Error - start entry in FloatingLabelNextString");
6426  }
6427  if(Ptr->FormatType == TimeTimeLoc)
6428  {
6429  if(TrainMode == Timetable)
6430  {
6431  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6432  // not arrived yet in tt mode
6433  {
6434  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
6435  }
6436  else
6437  {
6438  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
6439  }
6440  }
6441  else // TrainMode == Signaller
6442  {
6443  if(!DepartureTimeSet) // not arrived yet
6444  {
6445  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
6446  }
6447  else
6448  {
6449  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
6450  }
6451  }
6452  }
6453  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6454  {
6455  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
6456  }
6457  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6458  {
6459  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
6460  }
6461  else if(Ptr->FormatType == PassTime) // new
6462  {
6463  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
6464  }
6465  else if(Ptr->Command == "Fns")
6466  {
6467  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
6468  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
6469  RetStr = CheckNewServiceDepartureTime(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6470  }
6471  else if(Ptr->Command == "F-nshs")
6472  {
6473  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
6475  RetStr = CheckNewServiceDepartureTime(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6476  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6477  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6478  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6479  }
6480  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6481  {
6482  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6483  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
6484  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6485  RetStr = CheckNewServiceDepartureTime(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6486  }
6487  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6488  {
6489  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6490  + " at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
6491  RetStr = CheckNewServiceDepartureTime(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6492  }
6493  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
6494  {
6495  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
6496  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
6497  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
6498  RetStr = CheckNewServiceDepartureTime(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
6499  }
6500  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6501  {
6502  RetStr ="None, train terminated at " + Ptr->LocationName;
6503  }
6504  else if(Ptr->Command == "Frh")
6505  {
6506  RetStr = "None, train terminated at " + Ptr->LocationName;
6507  }
6508  else if(Ptr->Command == "Fer")
6509  {
6510  AnsiString AllowedExits = "";
6511  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
6512  }
6513  else if(Ptr->Command == "Fjo")
6514  {
6515  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
6517  }
6518  else if(Ptr->Command == "jbo")
6519  {
6520  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6521  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
6522  }
6523  else if(Ptr->Command == "fsp")
6524  {
6525  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6526  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
6527  }
6528  else if(Ptr->Command == "rsp")
6529  {
6530  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
6531  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
6532  }
6533  else if(Ptr->Command == "cdt")
6534  {
6535  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
6536  }
6537  Utilities->CallLogPop(1124);
6538  return RetStr;
6539 }
6540 
6541 // ---------------------------------------------------------------------------
6542 
6543 AnsiString TTrain::CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
6544 {
6545  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
6546  + AnsiString(RptNum) + ",CheckNewServiceDepartureTime," + HeadCode);
6547  AnsiString DepTime = "", EventTime = "";
6548  bool CDTFlag = false; //reports if train changes direction before departs
6549  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
6550  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
6551  {
6552  if(AVI->Command == "cdt")
6553  {
6554  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
6555  continue;
6556  }
6557  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
6558  {
6559  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
6560  RetStr += "\nNew service splits at " + EventTime;
6561  Utilities->CallLogPop(2234);
6562  return RetStr;
6563  }
6564  if(AVI->Command == "jbo")
6565  {
6566  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
6567  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
6568  Utilities->CallLogPop(2235);
6569  return RetStr;
6570  }
6571  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
6572  {
6573  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
6574  if(CDTFlag)
6575  {
6576  RetStr += "\nNew service changes direction then departs at " + DepTime;
6577  }
6578  else
6579  {
6580  RetStr += "\nNew service departs at " + DepTime;
6581  }
6582  Utilities->CallLogPop(2236);
6583  return RetStr;
6584  }
6585  }
6586  Utilities->CallLogPop(2208);
6587  return RetStr; //if reach here then RetStr doesn't change
6588 }
6589 
6590 // ---------------------------------------------------------------------------
6591 
6593  // Enter with Ptr pointing to first action to be listed (i.e. next action)
6594 {
6595  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6596  ",FloatingTimetableString" + "," + HeadCode);
6597  AnsiString RetStr = "", PartStr = "";
6598  int Count = 0;
6599 
6600  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
6601  // can start in signaller control so exclude this
6602  {
6603  throw Exception("Error - start entry in FloatingTimetableString");
6604  }
6605  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTime Loc check later
6606  bool FirstPass = true;
6607  Ptr--; // because incremented at start of loop
6608 
6609  // different first TimeTimeLoc display if in signaller control
6610  do
6611  {
6612  Ptr++;
6613  if((Ptr->FormatType == Repeat) || TimetableFinished)
6614  break;
6615  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
6616  {
6617  AnsiString TrainLoc = "";
6618  if(TrainMode == Timetable)
6619  {
6620  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
6621  {
6622  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6623  }
6624  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6625  {
6626  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6627  }
6628  else
6629  {
6630  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6631  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6632  Count++; // because there are 2 entries
6633  }
6634  }
6635  else // TrainMode == Signaller
6636  {
6637  if(DepartureTimeSet)
6638  {
6639  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6640  }
6641  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6642  {
6643  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6644  }
6645  else
6646  {
6647  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6648  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6649  Count++; // because there are 2 entries
6650  }
6651  }
6652  }
6653  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
6654  {
6655  AnsiString TrainLoc = "";
6656  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
6657  {
6658  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6659  }
6660  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
6661  {
6662  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
6663  }
6664  else
6665  {
6666  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
6667  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6668  Count++; // because there are 2 entries
6669  }
6670  }
6671  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
6672  {
6673  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
6674  }
6675  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6676  {
6677  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
6678  }
6679  else if(Ptr->FormatType == PassTime) // new
6680  {
6681  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
6682  }
6683  else if(Ptr->Command == "Fns")
6684  {
6685  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
6686  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6687  PartStr = CheckNewServiceDepartureTime(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
6688  }
6689  else if(Ptr->Command == "F-nshs")
6690  {
6691  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
6692  Ptr->LocationName;
6693  PartStr = CheckNewServiceDepartureTime(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6694  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
6695  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
6696  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
6697  }
6698  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
6699  {
6700  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
6701  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
6702  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
6703  PartStr = CheckNewServiceDepartureTime(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6704  }
6705  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6706  {
6707  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
6708  + " at " + Ptr->LocationName;
6709  PartStr = CheckNewServiceDepartureTime(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6710  }
6711  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
6712  {
6713  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
6714  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
6715  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
6716  PartStr = CheckNewServiceDepartureTime(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
6717  }
6718  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
6719  {
6720  PartStr = "Terminate at " + Ptr->LocationName;
6721  }
6722  else if(Ptr->Command == "Frh")
6723  {
6724  PartStr = "Terminate at " + Ptr->LocationName;
6725  }
6726  else if(Ptr->Command == "Fer")
6727  {
6728  AnsiString AllowedExits = "";
6729  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
6730  }
6731  else if(Ptr->Command == "Fjo")
6732  {
6733  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
6734  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6735  }
6736  else if(Ptr->Command == "jbo")
6737  {
6738  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
6739  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6740  }
6741  else if(Ptr->Command == "fsp")
6742  {
6743  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
6744  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6745  }
6746  else if(Ptr->Command == "rsp")
6747  {
6748  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
6749  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
6750  }
6751  else if(Ptr->Command == "cdt")
6752  {
6753  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
6754  }
6755  if(RetStr != "")
6756  RetStr = RetStr + '\n' + PartStr;
6757  else
6758  RetStr = PartStr;
6759  FirstPass = false;
6760  Count++;
6761  }
6762  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
6763  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
6764  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
6765  // forward as anyone should wish to see without looking at the full timetable
6766  if(TimetableFinished)
6767  {
6768  if(TrainMode == Timetable)
6769  RetStr = "Timetable finished";
6770  else
6771  RetStr = "No timetable";
6772  }
6773  Utilities->CallLogPop(1125);
6774  return RetStr;
6775 }
6776 
6777 // ---------------------------------------------------------------------------
6778 
6779 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
6780 {
6781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
6782  Utilities->SaveFileString(OutFile, HeadCode);
6785  Utilities->SaveFileInt(OutFile, StartSpeed);
6788  Utilities->SaveFileInt(OutFile, RepeatNumber);
6791  Utilities->SaveFileInt(OutFile, Mass);
6794  Utilities->SaveFileDouble(OutFile, EntrySpeed);
6801  Utilities->SaveFileDouble(OutFile, BrakeRate);
6805  Utilities->SaveFileDouble(OutFile, double(EntryTime));
6806  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
6807  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
6808  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
6809  Utilities->SaveFileDouble(OutFile, double(TRSTime));
6810  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
6814  Utilities->SaveFileInt(OutFile, (short)TrainMode);
6819  Utilities->SaveFileBool(OutFile, Derailed);
6821  Utilities->SaveFileBool(OutFile, Crashed);
6828  Utilities->SaveFileBool(OutFile, NotInService);
6829  Utilities->SaveFileBool(OutFile, Plotted);
6830  Utilities->SaveFileBool(OutFile, TrainGone);
6831  Utilities->SaveFileBool(OutFile, SPADFlag);
6833  Utilities->SaveFileInt(OutFile, HOffset[0]);
6834  Utilities->SaveFileInt(OutFile, HOffset[1]);
6835  Utilities->SaveFileInt(OutFile, HOffset[2]);
6836  Utilities->SaveFileInt(OutFile, HOffset[3]);
6837  Utilities->SaveFileInt(OutFile, VOffset[0]);
6838  Utilities->SaveFileInt(OutFile, VOffset[1]);
6839  Utilities->SaveFileInt(OutFile, VOffset[2]);
6840  Utilities->SaveFileInt(OutFile, VOffset[3]);
6841  Utilities->SaveFileInt(OutFile, PlotElement[0]);
6842  Utilities->SaveFileInt(OutFile, PlotElement[1]);
6843  Utilities->SaveFileInt(OutFile, PlotElement[2]);
6844  Utilities->SaveFileInt(OutFile, PlotElement[3]);
6845  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
6846  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
6847  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
6848  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
6850  Utilities->SaveFileInt(OutFile, (short)Straddle);
6851  Utilities->SaveFileInt(OutFile, NextTrainID);
6852  Utilities->SaveFileInt(OutFile, TrainID);
6853  Utilities->SaveFileInt(OutFile, LeadElement);
6854  Utilities->SaveFileInt(OutFile, LeadEntryPos);
6855  Utilities->SaveFileInt(OutFile, LeadExitPos);
6856  Utilities->SaveFileInt(OutFile, MidElement);
6857  Utilities->SaveFileInt(OutFile, MidEntryPos);
6858  Utilities->SaveFileInt(OutFile, MidExitPos);
6859  Utilities->SaveFileInt(OutFile, LagElement);
6860  Utilities->SaveFileInt(OutFile, LagEntryPos);
6861  Utilities->SaveFileInt(OutFile, LagExitPos);
6862  int ColourNumber;
6863 
6865  ColourNumber = 0;
6867  ColourNumber = 1;
6869  ColourNumber = 2;
6871  ColourNumber = 3;
6873  ColourNumber = 4;
6875  ColourNumber = 5;
6877  ColourNumber = 6;
6879  ColourNumber = 7;
6881  ColourNumber = 8;
6883  ColourNumber = 9;
6885  ColourNumber = 10;
6887  ColourNumber = 11;
6889  ColourNumber = 12;
6890  else if(BackgroundColour == clTRSBackground)
6891  ColourNumber = 13;
6893  ColourNumber = 14; // added at v2.4.0
6894  Utilities->SaveFileInt(OutFile, ColourNumber);
6895 
6896  // additional data
6897  bool ForwardHeadCode;
6898 
6899  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
6900  {
6901  ForwardHeadCode = true;
6902  }
6903  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
6904  else
6905  {
6906  ForwardHeadCode = false;
6907  }
6908  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
6909 
6910  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
6911 
6912  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
6913  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
6914 
6915  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
6916  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
6917  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
6918  // so use the last asterisk position for this - 0 for false & 1 for true
6919  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
6920  AnsiString Marker;
6921 
6923  Marker = "*****1";
6924  else
6925  Marker = "*****0";
6926  if(RestoreTimetableLocation == "")
6927  Utilities->SaveFileString(OutFile, Marker);
6928  else
6929  {
6930  AnsiString CombinedString = RestoreTimetableLocation + Marker;
6931  Utilities->SaveFileString(OutFile, CombinedString);
6932  // RestoreTimetableLocation + marker
6933  }
6934  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
6935  Utilities->CallLogPop(1457);
6936 }
6937 
6938 // ---------------------------------------------------------------------------
6939 
6940 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
6941 {
6942  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
6943  HeadCode = Utilities->LoadFileString(InFile);
6946  StartSpeed = Utilities->LoadFileInt(InFile);
6948  if(SignallerMaxSpeed < 10)
6949  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
6951  RepeatNumber = Utilities->LoadFileInt(InFile);
6954  Mass = Utilities->LoadFileInt(InFile);
6958  // above added at v2.1.0 for legacy session files where value may not have been limited
6960  EntrySpeed = Utilities->LoadFileDouble(InFile);
6964  if(TimetableMaxRunningSpeed < 10)
6965  {
6966  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
6967  }
6969  if(MaxRunningSpeed < 10)
6970  {
6971  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
6972  }
6975  BrakeRate = Utilities->LoadFileDouble(InFile);
6979  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
6980  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
6981  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
6982  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
6983  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
6984  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
6993  Derailed = Utilities->LoadFileBool(InFile);
6995  Crashed = Utilities->LoadFileBool(InFile);
7002  NotInService = Utilities->LoadFileBool(InFile);
7003  Plotted = Utilities->LoadFileBool(InFile);
7004  TrainGone = Utilities->LoadFileBool(InFile);
7005  SPADFlag = Utilities->LoadFileBool(InFile);
7007  HOffset[0] = Utilities->LoadFileInt(InFile);
7008  HOffset[1] = Utilities->LoadFileInt(InFile);
7009  HOffset[2] = Utilities->LoadFileInt(InFile);
7010  HOffset[3] = Utilities->LoadFileInt(InFile);
7011  VOffset[0] = Utilities->LoadFileInt(InFile);
7012  VOffset[1] = Utilities->LoadFileInt(InFile);
7013  VOffset[2] = Utilities->LoadFileInt(InFile);
7014  VOffset[3] = Utilities->LoadFileInt(InFile);
7015  PlotElement[0] = Utilities->LoadFileInt(InFile);
7016  PlotElement[1] = Utilities->LoadFileInt(InFile);
7017  PlotElement[2] = Utilities->LoadFileInt(InFile);
7018  PlotElement[3] = Utilities->LoadFileInt(InFile);
7019  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7020  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7021  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7022  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7024  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7025  NextTrainID = Utilities->LoadFileInt(InFile);
7026  // will be same for all but best to save all anyway
7027  TrainID = Utilities->LoadFileInt(InFile);
7028  LeadElement = Utilities->LoadFileInt(InFile);
7029  LeadEntryPos = Utilities->LoadFileInt(InFile);
7030  LeadExitPos = Utilities->LoadFileInt(InFile);
7031  MidElement = Utilities->LoadFileInt(InFile);
7032  MidEntryPos = Utilities->LoadFileInt(InFile);
7033  MidExitPos = Utilities->LoadFileInt(InFile);
7034  LagElement = Utilities->LoadFileInt(InFile);
7035  LagEntryPos = Utilities->LoadFileInt(InFile);
7036  LagExitPos = Utilities->LoadFileInt(InFile);
7037  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7038 
7039  if(ColourNumber == 0)
7041  else if(ColourNumber == 1)
7043  else if(ColourNumber == 2)
7045  else if(ColourNumber == 3)
7047  else if(ColourNumber == 4)
7049  else if(ColourNumber == 5)
7051  else if(ColourNumber == 6)
7053  else if(ColourNumber == 7)
7055  else if(ColourNumber == 8)
7057  else if(ColourNumber == 9)
7059  else if(ColourNumber == 10)
7061  else if(ColourNumber == 11)
7063  else if(ColourNumber == 12)
7065  else if(ColourNumber == 13)
7067  else if(ColourNumber == 14)
7068  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7069 
7070  // additional data
7072  // sets the BackgroundColour to the loaded value
7073  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7074 
7075  if(ForwardHeadCode)
7076  {
7077  for(int x = 0; x < 4; x++)
7078  {
7080  }
7081  }
7082  else
7083  {
7084  for(int x = 0; x < 4; x++)
7085  {
7086  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7087  }
7088  }
7089 
7090  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7091  if(TrainMode == Timetable)
7092  {
7093  if(Crashed)
7095  else
7097  }
7098  else
7101  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7102  if(Straddle == LeadMid)
7103  {
7104  if(LeadElement > -1)
7106  if(LeadElement > -1)
7108  if(MidElement > -1)
7110  if(MidElement > -1)
7112  }
7113  else if(Straddle == LeadMidLag)
7114  {
7115  if(LeadElement > -1)
7117  if(MidElement > -1)
7119  if(MidElement > -1)
7121  if(LagElement > -1)
7123  }
7124 
7125  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7126 
7127  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7128  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7129 
7130  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7131 
7132  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7133  if(LeadElement > -1)
7134  // need to include this in case train exiting & no lead element
7135  {
7137  {
7138  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7139  }
7140  }
7141  AValue = sqrt(2 * PowerAtRail / Mass);
7142 
7143  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7144 
7145  // possible RestoreTimetableLocation + Marker, where Marker is
7146  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7147  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7148  // added at beta v0.2e
7149  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7150  // name not allowed to include the '*' character
7151  {
7152  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7153  bool GiveMessagesFalse = false;
7154  bool CheckLocationsExistInRailwayTrue = true;
7155  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7156  { // otherwise take no action
7157  RestoreTimetableLocation = Location;
7158  }
7159  }
7160  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7161 
7162  StoppedWithoutPower = false;
7163  if(Marker[6] == '1')
7164  {
7165  StoppedWithoutPower = true;
7166  }
7167  Utilities->CallLogPop(1458);
7168 }
7169 
7170 // ---------------------------------------------------------------------------
7171 
7172 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7173 {
7175  return false; // HeadCode
7176 
7177  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7178  return false; // RearStartElement
7179 
7180  if(!Utilities->CheckFileInt(InFile, 0, 3))
7181  return false; // RearStartExitPos
7182 
7183  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7184  return false; // StartSpeed
7185 
7186  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7187  return false; // SignallerMaxSpeed
7188 
7189  if(!Utilities->CheckFileBool(InFile))
7190  return false; // HoldAtLocationInTTMode
7191 
7192  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7193  return false; // RepeatNumber (max 96 x 60 at 1 min intervals)
7194 
7195  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7196  return false; // IncrementalMinutes (max 96 x 60)
7197 
7198  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7199  return false; // IncrementalDigits
7200 
7201  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7202  return false; // Mass
7203 
7204  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7205  return false;
7206 
7207  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7208  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7209  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7210  return false; // FrontElementLength
7211 
7212  if(!Utilities->CheckFileDouble(InFile))
7213  return false; // EntrySpeed
7214 
7215  if(!Utilities->CheckFileDouble(InFile))
7216  return false; // ExitSpeedHalf
7217 
7218  if(!Utilities->CheckFileDouble(InFile))
7219  return false; // ExitSpeedFull
7220 
7221  if(!Utilities->CheckFileDouble(InFile))
7222  return false; // TimetableMaxRunningSpeed
7223 
7224  if(!Utilities->CheckFileDouble(InFile))
7225  return false; // MaxRunningSpeed
7226 
7227  if(!Utilities->CheckFileDouble(InFile))
7228  return false; // MaxExitSpeed
7229 
7230  if(!Utilities->CheckFileDouble(InFile))
7231  return false; // MaxBrakeRate
7232 
7233  if(!Utilities->CheckFileDouble(InFile))
7234  return false; // BrakeRate
7235 
7236  if(!Utilities->CheckFileDouble(InFile))
7237  return false; // PowerAtRail
7238 
7239  if(!Utilities->CheckFileBool(InFile))
7240  return false; // FirstHalfMove
7241 
7242  if(!Utilities->CheckFileBool(InFile))
7243  return false; // OneLengthAccelDecel
7244 
7245  if(!Utilities->CheckFileDouble(InFile))
7246  return false; // double(EntryTime)
7247 
7248  if(!Utilities->CheckFileDouble(InFile))
7249  return false; // double(ExitTimeHalf)
7250 
7251  if(!Utilities->CheckFileDouble(InFile))
7252  return false; // double(ExitTimeFull)
7253 
7254  if(!Utilities->CheckFileDouble(InFile))
7255  return false; // double(ReleaseTime)
7256 
7257  if(!Utilities->CheckFileDouble(InFile))
7258  return false; // double(TRSTime)
7259 
7260  if(!Utilities->CheckFileDouble(InFile))
7261  return false; // double(LastActionTime)
7262 
7263  if(!Utilities->CheckFileBool(InFile))
7264  return false; // CallingOnFlag
7265 
7266  if(!Utilities->CheckFileBool(InFile))
7267  return false; // BeingCalledOn
7268 
7269  if(!Utilities->CheckFileBool(InFile))
7270  return false; // DepartureTimeSet
7271 
7272  if(!Utilities->CheckFileInt(InFile, 0, 2))
7273  return false; // (short)TrainMode
7274 
7275  if(!Utilities->CheckFileBool(InFile))
7276  return false; // TimetableFinished
7277 
7278  if(!Utilities->CheckFileBool(InFile))
7279  return false; // LastActionDelayFlag
7280 
7281  if(!Utilities->CheckFileBool(InFile))
7282  return false; // SignallerRemoved
7283 
7284  if(!Utilities->CheckFileBool(InFile))
7285  return false; // TerminatedMessageSent
7286 
7287  if(!Utilities->CheckFileBool(InFile))
7288  return false; // Derailed
7289 
7290  if(!Utilities->CheckFileBool(InFile))
7291  return false; // DerailPending
7292 
7293  if(!Utilities->CheckFileBool(InFile))
7294  return false; // Crashed
7295 
7296  if(!Utilities->CheckFileBool(InFile))
7297  return false; // StoppedAtBuffers
7298 
7299  if(!Utilities->CheckFileBool(InFile))
7300  return false; // StoppedAtSignal
7301 
7302  if(!Utilities->CheckFileBool(InFile))
7303  return false; // StoppedAtLocation
7304 
7305  if(!Utilities->CheckFileBool(InFile))
7306  return false; // SignallerStopped
7307 
7308  if(!Utilities->CheckFileBool(InFile))
7309  return false; // StoppedAfterSPAD
7310 
7311  if(!Utilities->CheckFileBool(InFile))
7312  return false; // StoppedForTrainInFront
7313 
7314  if(!Utilities->CheckFileBool(InFile))
7315  return false; // NotInService
7316 
7317  if(!Utilities->CheckFileBool(InFile))
7318  return false; // Plotted
7319 
7320  if(!Utilities->CheckFileBool(InFile))
7321  return false; // TrainGone
7322 
7323  if(!Utilities->CheckFileBool(InFile))
7324  return false; // SPADFlag
7325 
7326  if(!Utilities->CheckFileBool(InFile))
7327  return false; // TimeTimeLocArrived
7328 
7329  if(!Utilities->CheckFileInt(InFile, 0, 15))
7330  return false; // HOffset[0]
7331 
7332  if(!Utilities->CheckFileInt(InFile, 0, 15))
7333  return false; // HOffset[1]
7334 
7335  if(!Utilities->CheckFileInt(InFile, 0, 15))
7336  return false; // HOffset[2]
7337 
7338  if(!Utilities->CheckFileInt(InFile, 0, 15))
7339  return false; // HOffset[3]
7340 
7341  if(!Utilities->CheckFileInt(InFile, 0, 15))
7342  return false; // VOffset[0]
7343 
7344  if(!Utilities->CheckFileInt(InFile, 0, 15))
7345  return false; // VOffset[1]
7346 
7347  if(!Utilities->CheckFileInt(InFile, 0, 15))
7348  return false; // VOffset[2]
7349 
7350  if(!Utilities->CheckFileInt(InFile, 0, 15))
7351  return false; // VOffset[3]
7352 
7353  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7354  return false; // PlotElement[0]
7355 
7356  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7357  return false; // PlotElement[1]
7358 
7359  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7360  return false; // PlotElement[2]
7361 
7362  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7363  return false; // PlotElement[3]
7364 
7365  if(!Utilities->CheckFileInt(InFile, 0, 3))
7366  return false; // PlotEntryPos[0]
7367 
7368  if(!Utilities->CheckFileInt(InFile, 0, 3))
7369  return false; // PlotEntryPos[1]
7370 
7371  if(!Utilities->CheckFileInt(InFile, 0, 3))
7372  return false; // PlotEntryPos[2]
7373 
7374  if(!Utilities->CheckFileInt(InFile, 0, 3))
7375  return false; // PlotEntryPos[3]
7376 
7377  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7378  return false; // TrainCrashedInto
7379 
7380  if(!Utilities->CheckFileInt(InFile, 0, 2))
7381  return false; // (short)Straddle
7382 
7383  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7384  return false; // NextTrainID
7385 
7386  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7387  return false; // TrainID
7388 
7389  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7390  return false; // LeadElement
7391 
7392  if(!Utilities->CheckFileInt(InFile, 0, 3))
7393  return false; // LeadEntryPos
7394 
7395  if(!Utilities->CheckFileInt(InFile, 0, 3))
7396  return false; // LeadExitPos
7397 
7398  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7399  return false; // MidElement
7400 
7401  if(!Utilities->CheckFileInt(InFile, 0, 3))
7402  return false; // MidEntryPos
7403 
7404  if(!Utilities->CheckFileInt(InFile, 0, 3))
7405  return false; // MidExitPos
7406 
7407  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
7408  return false; // LagElement
7409 
7410  if(!Utilities->CheckFileInt(InFile, 0, 3))
7411  return false; // LagEntryPos
7412 
7413  if(!Utilities->CheckFileInt(InFile, 0, 3))
7414  return false; // LagExitPos
7415 
7416  if(!Utilities->CheckFileInt(InFile, 0, 14))
7417  return false;
7418 
7419  // Background colour number //14 is failed colour at v2.4.0
7420  if(!Utilities->CheckFileBool(InFile))
7421  return false; // ForwardHeadCode
7422 
7423  if(!Utilities->CheckFileInt(InFile, 0, 10000))
7424  return false; // TrainDataEntryValue
7425 
7426  if(!Utilities->CheckFileInt(InFile, 0, 10000))
7427  return false; // ActionVectorEntryValue
7428 
7430  return false; // End of train marker + possible RestoreTimetableLocation
7431 
7432  // and StoppedWithoutPower flag
7433  return true;
7434 }
7435 
7436 // ---------------------------------------------------------------------------
7437 
7438 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
7439 {
7440  // order below reflects significance so earlier shows first, as may have more than one flag set
7441  // only plot flashing trains when Flash is true
7442 
7443 /*
7444  clCrashedBackground (TColor)0x0000FF red
7445  clDerailedBackground (TColor)0x0000FF red
7446  clSPADBackground (TColor)0x00FFFF yellow
7447  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
7448  clCallOnBackground (TColor)0xFF33FF light magenta
7449  clSignalStopBackground (TColor)0x00FF66 green
7450  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
7451  clStationStopBackground (TColor)0xCCFFCC pale green
7452  clTRSBackground (TColor)0xFFCCFF light pink
7453  clBufferStopBackground (TColor)0xFFFFCC pale cyan
7454  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
7455  clSignallerStopped (TColor)0x99CCFF caramel
7456  clNormalBackground (TColor)0xCCCCCC grey
7457 */
7458 
7459  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
7460  bool HideFlashingTrain = true;
7461  // hide it when Flash false so it blinks on and off
7462  // if don't hide it it stays displayed all the time
7463  Graphics::TBitmap *SmallTrainBitmap;
7464 
7465  // NB ensure retain same order as zoomed in order so colours correspond
7467  {
7468  TrainController->CrashWarning = true;
7469  SmallTrainBitmap = RailGraphics->smRed;
7470  }
7472  {
7474  SmallTrainBitmap = RailGraphics->smRed;
7475  }
7477  {
7478  TrainController->SPADWarning = true;
7479  SmallTrainBitmap = RailGraphics->smYellow;
7480  }
7482  {
7484  SmallTrainBitmap = RailGraphics->smOrange;
7485  }
7487  {
7489  SmallTrainBitmap = RailGraphics->smMagenta;
7490  }
7492  {
7494  SmallTrainBitmap = RailGraphics->smBrightGreen;
7495  }
7497  {
7499  SmallTrainBitmap = RailGraphics->smCyan;
7500  }
7502  {
7503  SmallTrainBitmap = RailGraphics->smPaleGreen;
7504  HideFlashingTrain = false;
7505  }
7507  {
7508  SmallTrainBitmap = RailGraphics->smCyan;
7509  HideFlashingTrain = false;
7510  }
7512  {
7513  SmallTrainBitmap = RailGraphics->smLightBlue;
7514  HideFlashingTrain = false;
7515  }
7517  {
7518  SmallTrainBitmap = RailGraphics->smCaramel;
7519  HideFlashingTrain = false;
7520  }
7521  else
7522  {
7523  SmallTrainBitmap = RailGraphics->smBlack; // moving
7524  HideFlashingTrain = false;
7525  }
7526 
7527  // now plot the new train
7528  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
7529  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
7530  {
7531  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
7532  }
7533  if((MidElement > -1) && (!HideFlashingTrain || Flash))
7534  {
7535  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
7536  }
7537  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
7538  {
7539  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
7540  }
7544  Utilities->CallLogPop(1459);
7545 }
7546 
7547 // ---------------------------------------------------------------------------
7548 
7550 {
7551  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
7552  if(!Display->ZoomOutFlag)
7553  {
7554  Utilities->CallLogPop(1304);
7555  return;
7556  }
7557  for(int y = 0; y < 3; y++)
7558  {
7559  if(OldZoomOutElement[y] > -1)
7560  {
7561  bool FoundFlag = false;
7562  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
7563  TTrackElement IATElement1, IATElement2;
7564  // default elements to begin with
7565  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
7566  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
7567  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
7568  if(FoundFlag)
7569  {
7570  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
7571  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
7572  if(IMPair.first != IMPair.second)
7573  {
7574  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
7575  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
7576  }
7577  }
7578  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
7579  }
7580  }
7581  Utilities->CallLogPop(1305);
7582 }
7583 
7584 // ---------------------------------------------------------------------------
7585 
7586 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
7587 {
7588  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
7589  LocationName = "";
7590  if(!StoppedAtLocation)
7591  {
7592  Utilities->CallLogPop(1398);
7593  return false;
7594  }
7595  if(LeadElement > -1)
7596  {
7598  }
7599  if((LocationName == "") && (MidElement > -1))
7600  {
7601  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
7602  }
7603  if((LocationName == "") && (LagElement > -1))
7604  {
7605  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
7606  }
7607  if(LocationName == "")
7608  {
7609  throw Exception("Error - Location name not set in TrainAtLocation");
7610  }
7611  Utilities->CallLogPop(1399);
7612  return true;
7613 }
7614 
7615 // ---------------------------------------------------------------------------
7616 
7617 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
7618 {
7619  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
7620  for(int x = 0; x < 4; x++)
7621  {
7622  PlotTrainGraphic(7, x, Disp);
7623  }
7624  Utilities->CallLogPop(647);
7625 }
7626 
7627 // ---------------------------------------------------------------------------
7628 
7629 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
7630 {
7631  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
7632  for(int x = 0; x < 4; x++)
7633  {
7634  if(PlotElement[x] > -1)
7635  {
7636  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
7637  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
7638  }
7639  }
7640  Utilities->CallLogPop(1708);
7641 }
7642 
7643 // ---------------------------------------------------------------------------
7644 
7645 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
7646 {
7647  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
7648  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
7649  AnsiString(LinkNumber) + "," + HeadCode);
7650 
7651 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
7652  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
7653  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
7654 */
7655 
7656  // note that MidElement always fully occupied
7657  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
7658  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
7659  {
7660  Utilities->CallLogPop(2005);
7661  return true;
7662  }
7663  if(Straddle == LeadMid)
7664  {
7665  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
7666  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
7667  {
7668  Utilities->CallLogPop(2006);
7669  return true;
7670  }
7671  }
7672  else if(Straddle == LeadMidLag)
7673  {
7674  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
7675  // only interested in LeadEntryPos as train not occupying ExitPos yet
7676  {
7677  Utilities->CallLogPop(2007);
7678  return true;
7679  }
7680  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
7681  // only interested in LagExitPos as train has left EntryPos
7682  {
7683  Utilities->CallLogPop(2008);
7684  return true;
7685  }
7686  }
7687  Utilities->CallLogPop(2009);
7688  return false;
7689 }
7690 
7691 // ---------------------------------------------------------------------------
7692 
7693 float TTrain::CalcTimeToAct(int Caller) // only called for running trains
7698 {
7699  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
7700  int DistanceToRedSignal = 0;
7701  float TimeToAct = 0;
7702  float MinsEarly = 0; //added at v2.6.1
7703  TDateTime DepartureTime; //added at v2.6.1
7704 
7705  if(TrainFailed)
7706  {
7707  Utilities->CallLogPop(2147);
7708  return 0; // time to act now
7709  }
7710 
7711  if(SignallerStopped)
7712  {
7713  Utilities->CallLogPop(2080);
7714  return -1;
7715  }
7716 
7717  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
7718  {
7719  Utilities->CallLogPop(2266);
7720  return -1;
7721  }
7722 
7723  if(!Stopped() || StoppedAtLocation)
7724  {
7725  // calc distance to next red signal but check for continuation exit
7726  if(LeadElement == -1)
7727  // if -1 it's on an end element so no action needed
7728  {
7729  Utilities->CallLogPop(2075);
7730  return -1;
7731  }
7732  else
7733  {
7734  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
7735  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
7736  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
7737 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
7738  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
7739  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
7740  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
7741  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
7742  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
7743  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
7744  before the train has stopped the current station is still recognised as a future stop.
7745  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
7746  becomes the signal, and the time to act indication becomes 'NOW'.
7747 */
7748  {
7749  FirstPosToBeMeasured = LeadElement;
7750  FirstEntryPos = LeadEntryPos;
7751  }
7752  float CurrentStopTime; // set to 0 at start of function
7753  float LaterStopTime; // set to 0 at start of function
7754  float RecoverableTime; // set to 0 at start of function
7755  int AvTrackSpeed; // set to zero at start of function
7756  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
7757  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
7758  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
7759  if(DistanceToRedSignal == -1) // -1 for no action needed
7760  {
7761  Utilities->CallLogPop(2076);
7762  return -1;
7763  }
7764 /* Have MinsDelayed; pos or neg,
7765  CurrentStopTime; pos or zero
7766  LaterStopTime; pos or zero
7767  RecoverableTime; pos or zero
7768 
7769  & from these calculate TotalStopTime. noting that:
7770  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
7771  RecoverableTime always < LaterStopTime or both zero
7772  can't subtract more than RecoverableTime (MinsDelayed > 0)
7773  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
7774  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
7775  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
7776  if running early & stopped at location CurrentStopTime will automatically include the excess
7777 */
7778  float TimeToSubtract, TotalStopTime;
7779  if(MinsDelayed > RecoverableTime)
7780  TimeToSubtract = RecoverableTime;
7781  else
7782  TimeToSubtract = MinsDelayed; // may be negative;
7783 
7784  if((AvTrackSpeed > 0) && (DistanceToStationStop < DistanceToRedSignal) && (DistanceToStationStop > 0))//protection against div by zero, not needed of no stop before red signal, DistanceToStationStop != 0 as set to 0 if invalid
7785  {//added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for next station stop
7786  //first find departure time from the next stop
7788  {
7789  DepartureTime = ActionVectorEntryPtr->DepartureTime;
7790  }
7791  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
7792  {
7793  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
7794  // must be a departure
7795  DepartureTime = (ActionVectorEntryPtr + 1)->DepartureTime;
7796  }
7797  MinsEarly = (double(DepartureTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
7798  if(MinsEarly < 0)
7799  {
7800  MinsEarly = 0;
7801  }
7802  }
7803 
7804  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
7805  {
7806  if(CurrentStopTime > 0)
7807  TotalStopTime = CurrentStopTime + LaterStopTime;
7808  // stopped at loc, will depart on time
7809  else
7810  TotalStopTime = LaterStopTime - MinsDelayed;
7811  // not stopped, will depart on time at first later stop so add the delay
7812  }
7813  else if((MinsEarly > 0) && !Stopped()) //running early
7814  {
7815  TotalStopTime = LaterStopTime + MinsEarly;
7816  }
7817  else // on time or running late
7818  {
7819  if(LaterStopTime == 0)
7820  TotalStopTime = CurrentStopTime;
7821  // no later stops, if stopped now will depart as soon as possible,
7822  // if not stopped no stop times to add
7823  else
7824  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
7825  }
7826 
7827  if(AvTrackSpeed < 30)
7828  AvTrackSpeed = 30;
7829  int Speed = AvTrackSpeed;
7830  if(AvTrackSpeed > int(MaxRunningSpeed))
7831  Speed = int(MaxRunningSpeed);
7832  if(TrainMode == Signaller)
7833  {
7834  Speed = SignallerMaxSpeed;
7835  TotalStopTime = 0;
7836  }
7837  TimeToAct = TotalStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
7838  // accel & decel taken into account in
7839  // CalcDistanceToRedSignalandStopTime
7840  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
7841  Utilities->CallLogPop(2079);
7842  return TimeToAct;
7843  }
7844  }
7845  else // stopped not at location
7846  {
7848  TimeToAct = 0.0;
7849  // but if stopped at a signal & autosigs route after it then ok
7850  if(StoppedAtSignal)
7851  {
7852  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
7853  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
7854  int NextExitPos;
7855  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
7856  {
7857  if((NextEntryPos == 0) || (NextEntryPos == 2))
7858  // leading entry point
7859  {
7860  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
7861  NextExitPos = 1;
7862  else
7863  NextExitPos = 3;
7864  }
7865  else
7866  NextExitPos = 0; // trailing entry point
7867  }
7868  else
7869  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
7870  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
7871  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
7872  int RouteNumber; // holder for referenced value, not used
7873  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
7874  {
7875  TimeToAct = -1;
7876  }
7877  }
7878  Utilities->CallLogPop(2074);
7879  return TimeToAct;
7880  }
7881 }
7882 
7883 // ---------------------------------------------------------------------------
7884 
7886 {
7887  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
7888  if(LeadElement > -1)
7889  {
7891  {
7892  Utilities->CallLogPop(2148);
7893  return true;
7894  }
7895  }
7896  if(MidElement > -1)
7897  {
7899  {
7900  Utilities->CallLogPop(2149);
7901  return true;
7902  }
7903  }
7904  if(LagElement > -1)
7905  {
7907  {
7908  Utilities->CallLogPop(2150);
7909  return true;
7910  }
7911  }
7912  Utilities->CallLogPop(2151);
7913  return false;
7914 }
7915 
7916 // ---------------------------------------------------------------------------
7917 // TTrainController
7918 // ---------------------------------------------------------------------------
7919 
7921 {
7922  OnTimeArrivals = 0;
7923  LateArrivals = 0;
7924  EarlyArrivals = 0;
7925  OnTimePasses = 0;
7926  LatePasses = 0;
7927  EarlyPasses = 0;
7928  OnTimeDeps = 0;
7929  LateDeps = 0;
7930  MissedStops = 0;
7931  OtherMissedEvents = 0;
7932  UnexpectedExits = 0;
7933  NumFailures = 0;
7934  IncorrectExits = 0;
7935  SPADEvents = 0;
7936  SPADRisks = 0;
7937  CrashedTrains = 0;
7938  Derailments = 0;
7939  TotArrDepPass = 0;
7940  TotLateArrMins = 0;
7941  TotEarlyArrMins = 0;
7942  TotLatePassMins = 0;
7943  TotEarlyPassMins = 0;
7944  TotLateDepMins = 0;
7945  ExcessLCDownMins = 0;
7946  TTClockTime = 0; // added for v0.6
7948  // added at v1.3.0 to ensure false at start
7949  OpTimeToActUpdateCounter = 0; // new v2.2.0
7950  OpActionPanelVisible = false; // new v2.2.0
7951  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
7952  SSHigh = false;
7953  MRSHigh = false;
7954  MRSLow = false;
7955  MassHigh = false;
7956  BFHigh = false;
7957  BFLow = false;
7958  PwrHigh = false;
7959  SigSHigh = false;
7960  SigSLow = false;
7961  randomize();
7962  // to seed rand() & random() with a random number (see UpdateTrain)
7963 }
7964 
7965 // ---------------------------------------------------------------------------
7966 
7968 {
7969  for(unsigned int x = 0; x < TrainVector.size(); x++)
7970  {
7971  TrainVectorAt(32, x).DeleteTrain(4);
7972  }
7973  TrainVector.clear();
7974 }
7975 
7976 // ---------------------------------------------------------------------------
7977 
7978 void TTrainController::LogEvent(AnsiString Str)
7979 {
7980  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
7981 
7982  // restrict to last 1000 entries
7983  Utilities->EventLog.push_back(FullStr);
7984  if(Utilities->EventLog.size() > 1000)
7985  Utilities->EventLog.pop_front();
7986 }
7987 
7988 // ---------------------------------------------------------------------------
7989 
7991 {
7992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
7993  bool ClockState = Utilities->Clock2Stopped;
7994 
7995  Utilities->Clock2Stopped = true;
7996  // new section dealing with Snt & Snt-sh additions
7997  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
7998  // clock tick after stops flashing
8000  {
8001  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8002  {
8003  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
8004  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
8005  TActionEventType EventType = NoEvent;
8006  if(AVEntry0.Command == "Snt")
8007  {
8008  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8009  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8010  int IncrementalMinutes = 0;
8011  int IncrementalDigits = 0;
8012  if(AVEntryLast.FormatType == Repeat)
8013  {
8014  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8015  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8016  }
8017  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8018  {
8019  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
8020  }
8021  // see above note
8022 
8023  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8024  {
8025  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
8026  if(TTOD.RunningEntry != NotStarted)
8027  continue;
8028  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
8029  break; // all the rest will also be greater
8030  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
8031  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8032  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
8033  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
8034  {
8035  TTOD.TrainID = TrainVector.back().TrainID;
8036  TTOD.RunningEntry = Running;
8037  }
8038  else if(EventType == FailTrainEntry)
8039  {
8040  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8041  }
8042  }
8043  }
8044 
8045  if(AVEntry0.Command == "Snt-sh")
8046  // just start this once, shuttle repeats take care of restarts
8047  {
8048  // calc below only for Snt & Snt-sh entries rather than all entries to save time
8049  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
8050  int IncrementalMinutes = 0;
8051  int IncrementalDigits = 0;
8052  if(AVEntryLast.FormatType == Repeat)
8053  {
8054  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
8055  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
8056  }
8057  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
8058  {
8059  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
8060  }
8061  // see above note
8062  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
8063  if(TTOD.RunningEntry == NotStarted)
8064  {
8065  if(AVEntry0.EventTime <= TTClockTime)
8066  {
8067  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
8068  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
8069  TDEntry.SignallerSpeed, false, EventType))
8070  // false for SignallerControl
8071  {
8072  TTOD.TrainID = TrainVector.back().TrainID;
8073  TTOD.RunningEntry = Running;
8074  }
8075  else if(EventType == FailTrainEntry)
8076  {
8077  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
8078  }
8079  }
8080  }
8081  }
8082  }
8083  }
8084 
8085  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
8086  // iteration, next cycle will catch up with any other pending updates
8087  if(!TrainVector.empty())
8088  {
8089  TrainAdded = false;
8090  AllRoutes->CallonVector.clear();
8091  // this will be rebuilt during the calls to UpdateTrain
8092  for(unsigned int x = 0; x < TrainVector.size(); x++)
8093  {
8094  TrainVectorAt(33, x).UpdateTrain(0);
8095  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
8096  // added HasTrainGone() condition in v0.4c to prevent 2 trains both having TrainGone set
8097  // at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
8098  // iterates in reverse to erase the second train to have gone, but afterwards ReplotTrains iterates forwards and therefore replots the first train to
8099  // have gone and therefore sets the TrainIDOnElement value to the exited train, with nothing to reset it. Hovering the mouse over that element with
8100  // train information enabled causes an error because the track element thinks the train is still there, whereas it is missing from the TrainVector.
8101  {
8102  break;
8103  }
8104  }
8105  // set warning flags
8106  CrashWarning = false;
8107  DerailWarning = false;
8108  SPADWarning = false;
8109  CallOnWarning = false;
8110  SignalStopWarning = false;
8111  BufferAttentionWarning = false;
8112  TrainFailedWarning = false;
8113  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
8114  {
8115  TTrain &Train = TrainVectorAt(34, x);
8116  if(Train.Crashed)
8117  // can't use background colours for crashed & derailed because same colour
8118  {
8119  CrashWarning = true;
8120  }
8121  else if(Train.Derailed)
8122  // can't use background colours for crashed & derailed because same colour
8123  {
8124  DerailWarning = true;
8125  }
8126  else if(Train.BackgroundColour == clSPADBackground)
8127  // use colour as that changes as soon as passes signal
8128  {
8129  SPADWarning = true;
8130  }
8131  else if(Train.BackgroundColour == clTrainFailedBackground)
8132  {
8133  TrainFailedWarning = true;
8134  }
8135  else if(Train.BackgroundColour == clCallOnBackground)
8136  // use colour as also stopped at signal
8137  {
8138  CallOnWarning = true;
8139  }
8140  else if(Train.BackgroundColour == clSignalStopBackground)
8141  // use colour to distinguish from call-on
8142  {
8143  SignalStopWarning = true;
8144  }
8145  else if(Train.BackgroundColour == clBufferAttentionNeeded)
8146  // use colour to distinguish from ordinary buffer stop
8147  {
8148  BufferAttentionWarning = true;
8149  }
8150  if(Train.HasTrainGone())
8151  {
8152  AnsiString Loc = "";
8153  bool ElementFound = false;
8154  TTrackElement TE;
8155  if(Train.LagElement > -1)
8156  {
8157  TE = Track->TrackElementAt(531, Train.LagElement);
8158  ElementFound = true;
8159  }
8160  else if(Train.MidElement > -1)
8161  {
8162  TE = Track->TrackElementAt(779, Train.MidElement);
8163  ElementFound = true;
8164  }
8165  else if(Train.LeadElement > -1)
8166  {
8167  TE = Track->TrackElementAt(780, Train.LeadElement);
8168  ElementFound = true;
8169  }
8170  if(ElementFound)
8171  {
8172  if(TE.ActiveTrackElementName != "")
8173  {
8174  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8175  }
8176  else
8177  {
8178  Loc = "track element " + TE.ElementID;
8179  }
8180  }
8181 
8182  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
8183  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
8184  // need above first because may also have ActionVectorEntryPtr == "Fer"
8185  {
8186  Train.UnplotTrain(9);
8187  // added at v1.3.0 to reset signals after train removed from an autosigsroute
8189  {
8192  }
8193  // end of addition
8194  AllRoutes->RebuildRailwayFlag = true;
8195  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
8196  // correctly after a crash
8197  }
8198  else if(AVEntryPtr->Command == "Fer")
8199  {
8200  bool CorrectExit = false;
8201  if(!AVEntryPtr->ExitList.empty())
8202  {
8203  for(TExitListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
8204  {
8205  if(*ELIT == Train.LagElement)
8206  CorrectExit = true;
8207  }
8208  }
8209  if(CorrectExit)
8210  {
8211  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
8212  }
8213  else
8214  {
8215  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
8216  }
8217  }
8218  else
8219  {
8220  if(!AVEntryPtr->SignallerControl)
8221  {
8222  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
8223  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
8224  // -2 is marker for send messages for all remaining actions except Fer if present
8225  }
8226  else
8227  {
8228  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
8229  }
8230  }
8231  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
8232  Train.DeleteTrain(1);
8233  TrainVector.erase(TrainVector.begin() + x);
8234  ReplotTrains(1, Display);
8235  // to reset ElementIDs for remaining trains when have removed a train
8236  }
8237  }
8238  }
8239  else
8240  {
8241  // reset all flags in case last train removed with flag set
8242  CrashWarning = false;
8243  DerailWarning = false;
8244  SPADWarning = false;
8245  CallOnWarning = false;
8246  SignalStopWarning = false;
8247  BufferAttentionWarning = false;
8248  TrainFailedWarning = false;
8249  }
8250 
8251  // update OpTimeToActMultimap
8253  {
8255  // clears entries then adds values for running trains then for continuation entries
8256  }
8257 
8258  Utilities->Clock2Stopped = ClockState;
8259  Utilities->CallLogPop(723);
8260 }
8261 
8262 // ---------------------------------------------------------------------------
8264 {
8265  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
8266  if(!TrainVector.empty())
8267  {
8268  for(int x = TrainVector.size() - 1; x >= 0; x--)
8269  {
8270  TrainVectorAt(50, x).DeleteTrain(2);
8271  }
8272  TrainVector.clear();
8273  }
8274  if(!TrainDataVector.empty())
8275  {
8276  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
8277  {
8278  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
8279  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
8280  {
8281  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
8282  TOD.RunningEntry = NotStarted;
8283  TOD.TrainID = -1;
8284  TOD.EventReported = NoEvent;
8285  }
8286  }
8287  }
8288  Display->GetOutputLog1()->Caption = "";
8289  Display->GetOutputLog2()->Caption = "";
8290  Display->GetOutputLog3()->Caption = "";
8291  Display->GetOutputLog4()->Caption = "";
8292  Display->GetOutputLog5()->Caption = "";
8293  Display->GetOutputLog6()->Caption = "";
8294  Display->GetOutputLog7()->Caption = "";
8295  Display->GetOutputLog8()->Caption = "";
8296  Display->GetOutputLog9()->Caption = "";
8297  Display->GetOutputLog10()->Caption = "";
8298  Utilities->CallLogPop(1352);
8299 }
8300 
8301 // ---------------------------------------------------------------------------
8302 
8304 {
8305  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
8306  if(!TrainVector.empty())
8307  {
8308  for(unsigned int x = 0; x < TrainVector.size(); x++)
8309  {
8310  TrainVectorAt(51, x).PlotTrain(4, Disp);
8311  }
8312  }
8313  Utilities->CallLogPop(724);
8314 }
8315 
8316 // ---------------------------------------------------------------------------
8317 
8318 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
8319 {
8320  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
8321  if(!TrainVector.empty())
8322  {
8323  for(unsigned int x = 0; x < TrainVector.size(); x++)
8324  {
8325  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
8326  }
8327  }
8328  Utilities->CallLogPop(1707);
8329 }
8330 
8331 // ---------------------------------------------------------------------------
8332 
8334 {
8335  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
8336  if(!TrainVector.empty())
8337  {
8338  for(unsigned int x = 0; x < TrainVector.size(); x++)
8339  {
8340  TrainVectorAt(52, x).UnplotTrain(10);
8341  }
8342  }
8344  Utilities->CallLogPop(725);
8345 }
8346 
8347 // ---------------------------------------------------------------------------
8348 
8349 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
8350  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
8351  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
8352 {
8353  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
8354  "," + AnsiString(Mass) + "," + ModeStr);
8355  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
8356  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr + "," + HeadCode);
8357 
8358  int RearExitPos = -1;
8359 
8360  for(int x = 0; x < 4; x++)
8361  {
8362  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
8363  {
8364  RearExitPos = x;
8365  }
8366  }
8367  if(RearExitPos == -1)
8368  {
8369  throw Exception("Error, RearExit == -1 in AddTrain");
8370  }
8371 
8372  bool ReportFlag = true;
8373 
8374  // used to stop repeated messages from CheckStartAllowable when split failed
8375  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
8376  ReportFlag = false;
8377 
8378  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
8379  { // messages sent to performance log in CheckStartAllowable if ReportFlag true
8380  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
8381  Utilities->CallLogPop(938);
8382  return false;
8383  }
8384 
8385  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
8386  TTrainMode TrainMode = NoMode;
8387 
8388  if(ModeStr == "Timetable")
8389  TrainMode = Timetable;
8390  // all else gives 'None', 'Signaller' set within program
8391 
8392  if(MaxRunningSpeed < 10)
8393  {
8394  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8395  }
8396  if(SignallerSpeed < 10)
8397  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
8398  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
8399  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
8400 
8401  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
8402  // initialise here rather than in TTrain constructor as create trains
8403  // with Null TrainDataEntryPtr when loading session trains
8404  if(SignallerControl)
8405  {
8406  NewTrain->TimetableFinished = true;
8407  NewTrain->SignallerStoppingFlag = false;
8408  NewTrain->TrainMode = Signaller;
8409  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
8410  {
8411  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
8412  }
8414  }
8415 
8416  // deal with starting conditions:-
8417  // unlocated Snt: just report entry & advance pointer
8418  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
8419  // Sns doesn't need a new train
8420  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
8421  // covers all above located starts
8422  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
8423  // wouldn't have accepted the timetable
8424  {
8425  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
8426  // StoppedAtBuffers is set in UpdateTrain()
8427  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
8428  // buffer end must be ahead of train or would have failed start position check
8429  {
8430  NewTrain->StoppedAtLocation = true;
8431  NewTrain->PlotStartPosition(0);
8433  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
8434  NewTrain->ActionVectorEntryPtr->Warning);
8435  if(!SignallerControl) // don't advance if SignalControlEntry
8436  {
8437  NewTrain->ActionVectorEntryPtr++;
8438  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
8439  }
8440  NewTrain->LastActionTime = TTClockTime;
8441  }
8442  // else a through station stop
8443  else
8444  {
8445  NewTrain->StoppedAtLocation = true;
8446  NewTrain->PlotStartPosition(10);
8448  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
8449  NewTrain->ActionVectorEntryPtr->Warning);
8450  if(!SignallerControl) // don't advance if SignalControlEntry
8451  {
8452  NewTrain->ActionVectorEntryPtr++;
8453  }
8454  NewTrain->LastActionTime = TTClockTime;
8455  }
8456  }
8457  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
8458  {
8459  NewTrain->PlotStartPosition(11);
8460  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
8461  AnsiString Loc = "";
8462  if(TE.ActiveTrackElementName != "")
8463  {
8464  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
8465  }
8466  else
8467  {
8468  Loc = "track element " + TE.ElementID;
8469  }
8470  if(TE.TrackType == Continuation)
8471  {
8472  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
8473  }
8474  else
8475  {
8476  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
8477  }
8478  if(!SignallerControl) // don't advance if SignalControlEntry
8479  {
8480  NewTrain->ActionVectorEntryPtr++;
8481  }
8482  NewTrain->LastActionTime = TTClockTime;
8483  // no need to set LastActionTime for an unlocated entry
8484  }
8485 
8486  // cancel a wrong-direction route if either element of train starts on one
8487  if(NewTrain->LeadElement > -1)
8488  {
8489  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
8490  }
8491  if(NewTrain->MidElement > -1)
8492  {
8493  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
8494  }
8495 
8496  // set signals for a right-direction autosigs route for either element of train on one
8497  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
8498  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
8499  int RouteNumber = -1;
8500  bool SignalsSet = false;
8501 
8502  if(NewTrain->LeadElement > -1)
8503  {
8504  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8505  { // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
8506  int RouteStartPosition;
8507  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
8508  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
8509  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
8510  if(FirstPair.first == RouteNumber)
8511  {
8512  RouteStartPosition = FirstPair.second;
8513  }
8514  else if(SecondPair.first == RouteNumber)
8515  {
8516  RouteStartPosition = SecondPair.second;
8517  }
8518  else
8519  {
8520  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
8521  }
8522  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
8523  SignalsSet = true;
8524  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
8525  }
8526  else if(RouteNumber > -1) // non-autosigsroute
8527  {
8528  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
8529  int FirstTVPos = TempPDE.GetTrackVectorPosition();
8530  int FirstELinkPos = TempPDE.GetELinkPos();
8531  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
8532  {
8533  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8534  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
8535  }
8536  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
8537  {
8538  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8539  // remove the last element under LeadElement
8540  }
8541  AllRoutes->RebuildRailwayFlag = true;
8542  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
8543  // now deal with a rear linked autosigs route
8544  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
8545  {
8546  int LinkedRouteNumber = -1;
8547  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
8548  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
8549  {
8550  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
8551  // this is ok as here we are setting signals from the start of the route
8552  }
8553  }
8554  SignalsSet = true;
8555  }
8556  }
8557  if(NewTrain->MidElement > -1)
8558  // if entering at a continuation MidElement == -1
8559  { // this is included in case a train starts with LeadElement on no route and MidElement on a route
8560  if(!SignalsSet)
8561  {
8562  RouteNumber = -1;
8563  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8564  { // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
8565  int RouteStartPosition;
8566  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
8567  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
8568  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
8569  if(FirstPair.first == RouteNumber)
8570  {
8571  RouteStartPosition = FirstPair.second;
8572  }
8573  else if(SecondPair.first == RouteNumber)
8574  {
8575  RouteStartPosition = SecondPair.second;
8576  }
8577  else
8578  {
8579  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
8580  }
8581  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
8582  SignalsSet = true;
8583  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
8584  }
8585  else if(RouteNumber > -1) // non-autosigsroute
8586  {
8587  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
8588  int FirstTVPos = TempPDE.GetTrackVectorPosition();
8589  int FirstELinkPos = TempPDE.GetELinkPos();
8590  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
8591  {
8592  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8593  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
8594  }
8595  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
8596  {
8597  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
8598  // remove the last element under LeadElement
8599  }
8600  AllRoutes->RebuildRailwayFlag = true;
8601  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
8602  // now deal with a rear linked autosigs route
8603  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
8604  {
8605  int LinkedRouteNumber = -1;
8606  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
8607  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
8608  {
8609  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
8610  // this is ok as now we are setting signals from the start of the route
8611  }
8612  }
8613  }
8614  }
8615  }
8616 
8617  TrainVector.push_back(*NewTrain);
8618  Utilities->CallLogPop(731);
8619  return true;
8620 }
8621 
8622 // ---------------------------------------------------------------------------
8623 
8624 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
8625 {
8626  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
8627  AnsiString(TrackVectorNumber));
8628  int VecPos = -1;
8629 
8630  for(unsigned int x = 0; x < TrainVector.size(); x++)
8631  {
8632  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
8633  {
8634  VecPos = x;
8635  }
8636  }
8637  if(VecPos == -1)
8638  {
8639  throw Exception("Error, VecPos not set in EntryPos");
8640  }
8641  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
8642  {
8643  Utilities->CallLogPop(734);
8644  return TrainVectorAt(3, VecPos).LeadEntryPos;
8645  }
8646  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
8647  {
8648  Utilities->CallLogPop(735);
8649  return TrainVectorAt(5, VecPos).MidEntryPos;
8650  }
8651  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
8652  {
8653  Utilities->CallLogPop(736);
8654  return TrainVectorAt(7, VecPos).LagEntryPos;
8655  }
8656  Utilities->CallLogPop(737);
8657  return -1;
8658 }
8659 
8660 // ---------------------------------------------------------------------------
8661 
8663 {
8664  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
8665  for(unsigned int x = 0; x < TrainVector.size(); x++)
8666  {
8667  if(TrainVectorAt(53, x).TrainID == TrainID)
8668  {
8669  Utilities->CallLogPop(738);
8670  return TrainVectorAt(54, x);
8671  }
8672  }
8673  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
8674 }
8675 
8676 // ---------------------------------------------------------------------------
8677 
8678 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
8679  // return true if find the train (added at v2.4.0 as can select a removed train
8680  // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
8681 {
8682  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
8683  for(unsigned int x = 0; x < TrainVector.size(); x++)
8684  {
8685  if(TrainVectorAt(69, x).TrainID == TrainID)
8686  {
8687  Utilities->CallLogPop(2152);
8688  return true;
8689  }
8690  }
8691  Utilities->CallLogPop(2153);
8692  return false;
8693 }
8694 
8695 // ---------------------------------------------------------------------------
8696 
8697 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
8698 {
8699  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
8700  Utilities->Format96HHMMSS(Time));
8701  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
8702 
8703  Utilities->CallLogPop(2061);
8704  return RepeatTime;
8705 }
8706 
8707 // ---------------------------------------------------------------------------
8708 
8709 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
8710  // Enter with Ptr pointing to first action to be listed (i.e. next action)
8711 {
8712  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
8713  AnsiString RetStr = "", PartStr = "";
8714  int Count = 0;
8715  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
8716 
8717  Ptr--; // because incremented at start of loop
8718  do
8719  {
8720  Ptr++;
8721  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
8722  {
8723  continue; // move past the starting entry
8724  }
8725  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
8726  break;
8727  if(Ptr->SignallerControl)
8728  {
8729  RetStr = "Train under signaller control";
8730  break;
8731  }
8732  if(Ptr->FormatType == TimeTimeLoc)
8733  {
8734  if(Ptr->ArrivalTime == Ptr->DepartureTime)
8735  {
8736  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
8737  }
8738  else
8739  {
8740  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
8741  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
8742  Count++; // because there are 2 entries
8743  }
8744  }
8745  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
8746  {
8747  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
8748  }
8749  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
8750  {
8751  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
8752  }
8753  else if(Ptr->FormatType == PassTime) // new
8754  {
8755  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
8756  }
8757  else if(Ptr->Command == "Fns")
8758  {
8759  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8760  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8761  PartStr = ControllerCheckNewServiceDepartureTime(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
8762  }
8763  else if(Ptr->Command == "F-nshs")
8764  {
8765  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8766  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
8767  PartStr = ControllerCheckNewServiceDepartureTime(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8768  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
8769  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
8770  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
8771  }
8772 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
8773  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
8774  {
8775  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8776  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
8777  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
8778  PartStr = ControllerCheckNewServiceDepartureTime(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8779  }
8780  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
8781  {
8782  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8783  Ptr->NonRepeatingShuttleLinkHeadCode, + " at " + Ptr->LocationName;
8784  PartStr = ControllerCheckNewServiceDepartureTime(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8785  }
8786  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
8787  {
8788  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
8789  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
8790  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
8791  PartStr = ControllerCheckNewServiceDepartureTime(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
8792  }
8793  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
8794  {
8795  PartStr = "Terminate at " + Ptr->LocationName;
8796  }
8797  else if(Ptr->Command == "Frh")
8798  {
8799  PartStr = "Terminate at " + Ptr->LocationName;
8800  }
8801  else if(Ptr->Command == "Fer")
8802  {
8803  AnsiString AllowedExits;
8804  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
8805  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
8806  }
8807  else if(Ptr->Command == "Fjo")
8808  {
8809  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
8810  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8811  }
8812  else if(Ptr->Command == "jbo")
8813  {
8814  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
8815  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8816  }
8817  else if(Ptr->Command == "fsp")
8818  {
8819  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
8820  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8821  }
8822  else if(Ptr->Command == "rsp")
8823  {
8824  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
8825  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
8826  }
8827  else if(Ptr->Command == "cdt")
8828  {
8829  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
8830  }
8831  if(RetStr != "")
8832  RetStr = RetStr + '\n' + PartStr;
8833  else
8834  RetStr = PartStr;
8835  Count++;
8836  }
8837  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8838  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
8839  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8840  // forward as anyone should wish to see without looking at the full timetable
8841  Utilities->CallLogPop(2072);
8842  return RetStr;
8843 }
8844 
8845 // ---------------------------------------------------------------------------
8846 
8847 AnsiString TTrainController::ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
8848 {
8849  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
8850  + AnsiString(RptNum) + ",ControllerCheckNewServiceDepartureTime," + TDEPtr->HeadCode);
8851  AnsiString DepTime = "", EventTime = "";
8852  bool CDTFlag = false; //reports if train changes direction before departs
8853  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
8854  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
8855  {
8856  if(AVI->Command == "cdt")
8857  {
8858  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
8859  continue;
8860  }
8861  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
8862  {
8863  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
8864  RetStr += "\nNew service splits at " + EventTime;
8865  Utilities->CallLogPop(2237);
8866  return RetStr;
8867  }
8868  if(AVI->Command == "jbo")
8869  {
8870  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
8871  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
8872  Utilities->CallLogPop(2238);
8873  return RetStr;
8874  }
8875  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
8876  {
8877  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
8878  if(CDTFlag)
8879  {
8880  RetStr += "\nNew service changes direction then departs at " + DepTime;
8881  }
8882  else
8883  {
8884  RetStr += "\nNew service departs at " + DepTime;
8885  }
8886  Utilities->CallLogPop(2239);
8887  return RetStr;
8888  }
8889  }
8890  Utilities->CallLogPop(2223);
8891  return RetStr;
8892 }
8893 
8894 // ---------------------------------------------------------------------------
8895 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
8896 /*
8897  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
8898  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
8899  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
8900 
8901  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
8902  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
8903  user wishes
8904 
8905  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
8906  descriptive text or anything user wishes
8907  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
8908  be ignored) is taken as the timetable start time.
8909  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
8910  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
8911  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
8912  within the timetable if required.
8913  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
8914  services)
8915  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
8916  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
8917 
8918  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
8919  text timetable file easier
8920 
8921  form:-
8922  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
8923  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
8924  then multiple entries, separated by commas, of the form:-
8925 
8926  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
8927  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
8928  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
8929 
8930  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
8931  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
8932  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
8933 
8934  HH:MM;Command (cdt) }TimeCmd }
8935  HH:MM;Location (arr & dep) }TimeLoc }
8936  HH:MM;HH:MM;Location }TimeTimeLoc }
8937  HH:MM;pas;Location }PassTime }
8938  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
8939  HH:MM;Fer;set of allowable IDs }ExitRailway }
8940  Command (Frh only) }FinRemHere }
8941 
8942  R;mm;dd;nn. Repeat Repeat entry
8943 
8944  Formats:
8945 
8946  Command only: Frh
8947  Time;Command: cdt
8948  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
8949  Time;Command;2 Element IDs: Snt
8950  Time;Comand;n Element IDs: Fer
8951  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
8952  Time;Command;2 Element IDs;Headcode Snt-sh
8953  Time;Command;Location pas
8954  Time;Location Arr Dep
8955  Time;Time;Location Arr & dep together
8956 
8957  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
8958 
8959  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
8960  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
8961  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
8962 
8963  4 2x Linked entries, all shuttles:
8964 
8965  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
8966  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
8967  -> Remain Here (at finish location after all repeats)
8968  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
8969  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
8970 
8971  Allowable successors:-
8972 
8973  Successor state Type
8974 
8975  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
8976  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
8977  Sfs AtLoc )
8978  Sns AtLoc ) Start
8979  Sns-fsh AtLoc )
8980  Snt-sh AtLoc )
8981  Sns-sh AtLoc )
8982 
8983  pas Moving )
8984  jbo AtLoc )
8985  fsp AtLoc )
8986  rsp AtLoc ) Intermediate
8987  cdt AtLoc )
8988  TimeLoc arr Moving (bef) )
8989  TimeLoc dep AtLoc (bef) )
8990  TimeTimeLoc Moving )
8991 
8992  Fns Repeat/Nothing)
8993  Fjo Repeat/Nothing)
8994  Frh Repeat/Nothing)
8995  Fer Repeat/Nothing) Finish
8996  Frh-sh Repeat )
8997  Fns-sh Repeat )
8998  F-nshs Nothing )
8999 
9000  Descriptions:
9001  Snt New train
9002  Sfs New service from split
9003  Sns New service from another service
9004  Sns-fsh New non-repeating service from a shuttle service
9005  Snt-sh New shuttle train at a timetabled stop
9006  Sns-sh New shuttle service from a feeder service
9007 
9008  pas Pass
9009  jbo Be joined by another train
9010  fsp Front split
9011  rsp Rear split
9012  cdt Change direction of train
9013  TimeLoc arr Arrival
9014  TimeLoc dep Departure
9015  TimeTimeLoc Arrival and departure
9016 
9017  Fns Finish & form a new service
9018  Fjo Finish & join another train
9019  Frh Finish & remain here
9020  Fer Finish & exit railway
9021  Frh-sh Finish & repeat shuttle, finally remain here
9022  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
9023  F-nshs Finish & form a shuttle feeder service
9024 */
9025 
9026 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
9027 {
9028  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
9029  // a line that is too long; timetable containing too few lines; and timetable failed to open.
9030  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
9031  // new for v0.2b
9032  // compile ActiveTrackElementNameMap
9033  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
9034 
9036  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
9037  {
9038  // if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->TrackVector.at(x).TrackType != Continuation))
9039  if((Track->TrackVector.at(x).ActiveTrackElementName != "") && (Track->ContinuationNameMap.find(Track->TrackVector.at(x).ActiveTrackElementName))
9040  == Track->ContinuationNameMap.end())
9041  { // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
9042  ActiveTrackElementNameMapEntry.first = Track->TrackVector.at(x).ActiveTrackElementName;
9043  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
9044  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
9045  }
9046  }
9048  // end of new section
9049  std::ifstream TTBLFile(FileName, std::ios_base::binary);
9050 
9051  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
9052  if(TTBLFile.is_open())
9053  {
9054  char *TrainTimetableString = new char[10000];
9055  // enough for over 200 stations, should be adequate!
9056  bool EndOfFile = false;
9057  int Count = 0;
9058  // counts 'relevant' lines, i.e ignores any before the start time on its own line
9059  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9060  // delimiter is '\0' as it's an AnsiString
9061  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9062  // file empty - stores a null in 1st position if doesn't load any characters
9063  { // may still have eof even if read a line (no CRLF at end), and
9064  // if so need to process it
9065  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
9066  TTBLFile.close();
9067  delete TrainTimetableString;
9068  Utilities->CallLogPop(1611);
9069  return false;
9070  }
9071  AnsiString OneLine(TrainTimetableString);
9072  bool FinalCallFalse = false;
9073  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9074  // get rid of lines before the start time
9075  {
9076  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
9077  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9078  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9079  // stores a null in 1st position if doesn't load any characters
9080  { // may still have eof even if read a line (no CRLF at end), and
9081  // if so need to process it
9082  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
9083  TTBLFile.close();
9084  delete TrainTimetableString;
9085  Utilities->CallLogPop(772);
9086  return false;
9087  }
9088  OneLine = AnsiString(TrainTimetableString);
9089  }
9090  // here when have accepted the start time
9091  Count++; // increment past the start time
9092  while(!EndOfFile)
9093  {
9094  TTBLFile.getline(TrainTimetableString, 10000, '\0');
9095  // get next line after start time
9096  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
9097  // stores a null in 1st position if doesn't load any characters
9098  { // may still have eof even if read a line (no CRLF at end), and
9099  // if so need to process it
9100  EndOfFile = true;
9101  OneLine = "";
9102  }
9103  else
9104  OneLine = AnsiString(TrainTimetableString);
9105  if(OneLine.Length() > 9999)
9106  {
9107  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
9108  TTBLFile.close();
9109  delete TrainTimetableString;
9110  Utilities->CallLogPop(789);
9111  return false;
9112  }
9113  bool FinalCallFalse = false;
9114  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
9115  // false for FinalCall - just checking at this stage
9116  {
9117  TTBLFile.close();
9118  delete TrainTimetableString;
9119  Utilities->CallLogPop(770);
9120  return false;
9121  }
9122  if(EndOfFile && (Count < 2))
9123  // Timetable must contain at least two relevant lines, one for start time and at least one train
9124  {
9125  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
9126  TTBLFile.close();
9127  delete TrainTimetableString;
9128  Utilities->CallLogPop(771);
9129  return false;
9130  }
9131  Count++;
9132  }
9133  delete TrainTimetableString;
9134  TTBLFile.close();
9135  } // if(TTBLFile.is_open())
9136  else
9137  {
9138  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's not open in another application");
9139  Utilities->CallLogPop(2154);
9140  return false;
9141  }
9142  Utilities->CallLogPop(753);
9143  return true;
9144 }
9145 
9146 // ---------------------------------------------------------------------------
9147 
9148 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
9149  bool CheckLocationsExistInRailway) // return true for success
9150 
9151 /* Format:
9152  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
9153  descriptive text or anything user wishes
9154  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
9155  be ignored) is taken as the timetable start time.
9156  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
9157  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
9158  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
9159  within the timetable if required.
9160  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
9161  services)
9162  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
9163  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
9164 
9165  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
9166  text timetable file easier
9167 
9168  form:-
9169  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
9170  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
9171  then multiple entries, separated by commas, of the form:-
9172 
9173  Format FormatType
9174  [W]HH:MM;Command (cdt) }TimeCmd }
9175  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
9176  [W]HH:MM;pas;Location }PassTime }
9177  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
9178  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
9179  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
9180  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
9181  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
9182  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
9183  [W]HH:MM;Fns-sh;Details }FSHNewService }
9184  [W]HH:MM;Location (arr & dep) }TimeLoc }
9185  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
9186  Command (Frh only) }FinRemHere }
9187 
9188  R;mm;dd;nn. Repeat Repeat entry
9189 
9190  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
9191  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
9192  at location; or (c) departure time if train already at location (including train started at location either as a new
9193  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
9194  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
9195  minutes, incremental train headcode last 2 digits, and number of repeats.
9196 
9197  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
9198  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
9199  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
9200  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
9201  (it's for a shuttle train to return to depot at end of services)
9202 
9203  Command/Location & details are as follows:-
9204 
9205  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
9206  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
9207  2E44 in its Sfs entry. All these are checked.
9208  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
9209 
9210  Start commands:-
9211  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
9212  with loc as a start entry can't have a location as details)
9213  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
9214  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
9215  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
9216  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
9217  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
9218  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
9219  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
9220  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
9221  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
9222  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
9223  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
9224 
9225  Intermediate commands:-
9226  Time - Location (TimeLoc), can be arrival or departure depending on context
9227  Time Time location (TimeTimeLoc), arrival and departure
9228  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
9229  pas (PassTime), Time;pas;Location
9230  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
9231  joining train's finish details must correspond or the file check will fail
9232  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
9233  new train - that train's starting information must correspond)
9234  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
9235  new train - that train's starting information must correspond)
9236  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
9237 
9238  Finish commands:-
9239  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
9240  creation)
9241  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
9242  shuttle headcode (no train creation)
9243  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
9244  may have to wait for it), details = new headcode (delete train)
9245  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
9246  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
9247  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
9248  here
9249  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
9250  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
9251 
9252  Repeat:-
9253  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
9254  headcodes - it is up to user to avoid duplicates if he/she wishes to.
9255 
9256  Checks carried out with error messages in this function:-
9257  At least one comma in a service line (it's based on a .csv file)
9258  No entries following train information;
9259  At least one comma in remainder after train information (i.e at least a start and a finish entry);
9260  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
9261  First entry not a start entry;
9262  Train information incomplete before a start entry;
9263  Entry follows a finish entry but doesn't begin with 'R';
9264  SplitEntry returns false in a finish entry - message repeats the entry for information;
9265  Last action entry isn't a finish entry.
9266 
9267  Function returns false with no message if:-
9268  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
9269  time is found at all then an error message is given in the calling function);
9270  SplitTrainInfo returns false (message given in called function);
9271  SplitRepeat returns false (message given in called function).
9272 */ {
9273  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
9274  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
9275  TTrainDataEntry TempTrainDataEntry;
9276 
9277  EndOfFile = false;
9278  StripSpaces(0, OneLine);
9279  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
9280  // semicolons within the line
9281  ServiceReference = "";
9282  if(OneLine != "")
9283  {
9284  if(OneLine[1] != '*')
9285  {
9286  int SCPos = OneLine.Pos(';');
9287  if(SCPos == 0)
9288  ServiceReference = OneLine.SubString(1, 8);
9289  else
9290  ServiceReference = OneLine.SubString(1, (SCPos - 1));
9291  }
9292  }
9293  bool AllCommas = true;
9294 
9295  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
9296  {
9297  if(OneLine[x] != ',')
9298  AllCommas = false;
9299  }
9300  if(AllCommas || (OneLine == ""))
9301  {
9302  if(Count > 0)
9303  {
9304  EndOfFile = true;
9305  // returns true for a blank line - treated as end of file
9306  Utilities->CallLogPop(1018);
9307  return true;
9308  }
9309  else // count == 0 so not yet found a start time, no message to be given
9310  {
9311  Utilities->CallLogPop(754);
9312  return false;
9313  }
9314  }
9315  AnsiString First = "", Second = "", Third = "", Fourth = "";
9316  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
9317  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
9318  TDateTime StartTime(0);
9319  TExitList ExitList;
9320  bool Warning = false;
9321 
9322  if(Count == 0) // no start time found yet
9323  {
9324 /* dropped at v0.6b
9325  AnyHeadCodeValid = false;
9326  if(OneLine.SubString(6,5) == ";0000")
9327  {
9328  AnyHeadCodeValid = true;
9329  }
9330 */
9331  if(!CheckTimeValidity(0, OneLine, StartTime))
9332  {
9333  // no message is given for an invalid time as it's assumed to be an irrelevant line
9334  // if no start time is found at all then an error message is given in the calling function
9335  // AnyHeadCodeValid = false;
9336  Utilities->CallLogPop(755);
9337  return false;
9338  }
9339  if(FinalCall) // here if start time valid
9340  {
9341  TTClockTime = StartTime;
9342  TimetableStartTime = StartTime;
9343  }
9344  }
9345  else
9346  {
9347  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
9348  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
9349  double MaxBrakeRate = 0;
9350  double PowerAtRail = 0;
9351  int SignallerSpeed = 0;
9352  if(OneLine[1] == '*')
9353  {
9354  Utilities->CallLogPop(1581);
9355  return true;
9356  // ignore any line beginning with '*' but return true as there is no error
9357  }
9358  int Pos = OneLine.Pos(',');
9359  if(Pos == 0)
9360  {
9361  int SubStringLength = 20;
9362  if(OneLine.Length() < 20)
9363  SubStringLength = OneLine.Length();
9364  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
9365  Utilities->CallLogPop(766);
9366  return false;
9367  }
9368  TrainInfoStr = OneLine.SubString(1, Pos - 1);
9369  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
9370  GiveMessages)) // error messages given in SplitTrainInfo
9371  {
9372  Utilities->CallLogPop(773);
9373  return false;
9374  }
9375  if(FinalCall)
9376  {
9377  // store Train info - conversions done in SplitTrainInfo
9378  // only headcode mandatory for continuing services
9379  TempTrainDataEntry.HeadCode = HeadCode;
9380  TempTrainDataEntry.ServiceReference = HeadCode;
9381  TempTrainDataEntry.Description = Description;
9382  TempTrainDataEntry.StartSpeed = StartSpeed;
9383  TempTrainDataEntry.Mass = Mass;
9384  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
9385  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
9386  TempTrainDataEntry.PowerAtRail = PowerAtRail;
9387  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
9388  TTrainOperatingData TempTrainOperatingData;
9389  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
9390  }
9391  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
9392  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
9393  // so strip them off
9394  while(NewRemainder[NewRemainder.Length()] == ',')
9395  {
9396  if(NewRemainder.Length() > 1)
9397  {
9398  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
9399  }
9400  else
9401  {
9402  NewRemainder = "";
9403  break;
9404  }
9405  }
9406  // check if zero length & fail if so
9407  if(NewRemainder == "")
9408  {
9409  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
9410  Utilities->CallLogPop(769);
9411  return false;
9412  }
9413  // now have one more entry than there are commas
9414  int CommaCount = 0;
9415  for(int x = 1; x < NewRemainder.Length() + 1; x++)
9416  {
9417  if(NewRemainder[x] == ',')
9418  CommaCount++;
9419  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
9420  if(CommaCount == 0)
9421  {
9422  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
9423  {
9424  int SubStringLength = 20;
9425  if(OneLine.Length() < 20)
9426  SubStringLength = OneLine.Length();
9427  TimetableMessage(GiveMessages,
9428  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
9429  OneLine.SubString(1, SubStringLength) + "'....");
9430  Utilities->CallLogPop(783);
9431  return false;
9432  }
9433  }
9434  AnsiString OneEntry = "";
9435  TTimetableFormatType FormatType;
9436  TTimetableSequenceType SequenceType;
9437  TTimetableLocationType LocationType;
9438  TTimetableShuttleLinkType ShuttleLinkType;
9439  bool FinishFlag = false;
9440  for(int x = 0; x < CommaCount + 1; x++)
9441  {
9442  if((CommaCount == 0) || (x < CommaCount))
9443  // i.e. train entered under signaller control with no repeats, or entry is not the last,
9444  // in which case there's a comma & finish element or repeat still to come this entry could
9445  // be a finish but can't be a repeat
9446  {
9447  if(CommaCount == 0)
9448  {
9449  OneEntry = NewRemainder;
9450  NewRemainder = "";
9451  }
9452  else
9453  {
9454  Pos = NewRemainder.Pos(',');
9455  OneEntry = NewRemainder.SubString(1, Pos - 1);
9456  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
9457  }
9458  First = "";
9459  Second = "";
9460  Third = "";
9461  Fourth = "";
9462  RearStartOrRepeatMins = 0;
9463  FrontStartOrRepeatDigits = 0;
9464  NumberOfRepeats = 0;
9465  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
9466  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
9467  {
9468  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
9469  Utilities->CallLogPop(756);
9470  return false;
9471  }
9472  // check if warning for Frh or Fjo & reject
9473  if(Warning && (Second == "Frh"))
9474  {
9475  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
9476  Utilities->CallLogPop(1793);
9477  return false;
9478  }
9479  if(Warning && (Second == "Fjo"))
9480  {
9481  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
9482  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
9483  Utilities->CallLogPop(1794);
9484  return false;
9485  }
9486  if(x == 0) // should be start event
9487  {
9488  if(SequenceType != Start)
9489  {
9490  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
9491  Utilities->CallLogPop(784);
9492  return false;
9493  }
9494  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
9495  {
9496  if(NewRemainder[1] != 'R')
9497  {
9498  TimetableMessage(GiveMessages,
9499  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
9500  OneEntry + "'");
9501  Utilities->CallLogPop(787);
9502  return false;
9503  }
9504  }
9505  if((Second == "Snt") || (Second == "Snt-sh"))
9506  // need full train information including non-default values for at least HeadCode, Description,
9507  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
9508  {
9509  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
9510  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
9511  {
9512  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
9513  OneEntry + "'");
9514  Utilities->CallLogPop(1783);
9515  return false;
9516  }
9517  }
9518  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
9519  // service continuation - need at least non-default value for HeadCode
9520  {
9521  if(HeadCode == "")
9522  {
9523  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
9524  OneEntry + "'");
9525  Utilities->CallLogPop(788);
9526  return false;
9527  }
9528  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
9529  {
9530  TimetableMessage(GiveMessages,
9531  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
9532  OneEntry + "'");
9533  Utilities->CallLogPop(843);
9534  return false;
9535  }
9536  }
9537  }
9538  if(SequenceType == Finish)
9539  {
9540  FinishFlag = true;
9541  // marker for only permitted additional entry being a repeat, only needed if the
9542  // finish entry is not the last entry
9543  }
9544  if(FinalCall)
9545  {
9546  // interpret & add to ActionVector
9547  TDateTime TempTime;
9548  TActionVectorEntry ActionVectorEntry;
9549  ActionVectorEntry.FormatType = FormatType;
9550  ActionVectorEntry.LocationType = LocationType;
9551  ActionVectorEntry.SequenceType = SequenceType;
9552  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
9553  ActionVectorEntry.Warning = Warning;
9554  if(FormatType == TimeLoc)
9555  {
9556  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
9557  {;
9558  } // these will all be true as final call
9559  ActionVectorEntry.LocationName = Second;
9560  }
9561  else if(FormatType == PassTime) // new
9562  {
9563  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
9564  {;
9565  }
9566  ActionVectorEntry.Command = Second;
9567  ActionVectorEntry.LocationName = Third;
9568  }
9569  else if(FormatType == TimeTimeLoc)
9570  {
9571  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
9572  {;
9573  }
9574  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
9575  {;
9576  }
9577  ActionVectorEntry.LocationName = Third;
9578  }
9579  else if(FormatType == TimeCmd)
9580  {
9581  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
9582  {;
9583  }
9584  ActionVectorEntry.Command = Second;
9585  }
9586  else if(FormatType == ExitRailway)
9587  {
9588  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
9589  {;
9590  }
9591  ActionVectorEntry.Command = Second;
9592  ActionVectorEntry.ExitList = ExitList;
9593  }
9594  else if(FormatType == StartNew)
9595  {
9596  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
9597  {;
9598  }
9599  ActionVectorEntry.Command = Second;
9600  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9601  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9602  if(Fourth == 'S')
9603  ActionVectorEntry.SignallerControl = true;
9604  }
9605  else if(FormatType == SNTShuttle)
9606  {
9607  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
9608  {;
9609  }
9610  ActionVectorEntry.Command = Second;
9611  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9612  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9613  ActionVectorEntry.OtherHeadCode = Fourth;
9614  }
9615  else if(FormatType == SNSShuttle)
9616  {
9617  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
9618  {;
9619  }
9620  ActionVectorEntry.Command = Second;
9621  ActionVectorEntry.OtherHeadCode = Third;
9622  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9623  }
9624  else if(FormatType == TimeCmdHeadCode)
9625  {
9626  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
9627  {;
9628  }
9629  ActionVectorEntry.Command = Second;
9630  ActionVectorEntry.OtherHeadCode = Third;
9631  }
9632  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
9633  {
9634  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
9635  {;
9636  }
9637  ActionVectorEntry.Command = Second;
9638  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
9639  }
9640  else if(FormatType == FSHNewService)
9641  {
9642  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
9643  {;
9644  }
9645  ActionVectorEntry.Command = Second;
9646  ActionVectorEntry.OtherHeadCode = Third;
9647  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9648  }
9649  else if(FormatType == FinRemHere)
9650  {
9651  ActionVectorEntry.Command = Second;
9652  }
9653  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9654  }
9655  }
9656  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
9657  {
9658  OneEntry = NewRemainder;
9659  First = "";
9660  Second = "";
9661  Third = "";
9662  Fourth = "";
9663  RearStartOrRepeatMins = 0;
9664  FrontStartOrRepeatDigits = 0;
9665  NumberOfRepeats = 0;
9666  if((FinishFlag) && (OneEntry[1] != 'R'))
9667  // already had a finish entry
9668  {
9669  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
9670  Utilities->CallLogPop(79);
9671  return false;
9672  }
9673  if(OneEntry[1] != 'R') // must be finish
9674  {
9675  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
9676  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
9677  {
9678  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
9679  Utilities->CallLogPop(757);
9680  return false;
9681  }
9682  if(SequenceType != Finish)
9683  {
9684  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
9685  Utilities->CallLogPop(785);
9686  return false;
9687  }
9688  if(FinalCall)
9689  {
9690  // interpret & add to ActionVector
9691  TDateTime TempTime;
9692  TActionVectorEntry ActionVectorEntry;
9693  ActionVectorEntry.FormatType = FormatType;
9694  ActionVectorEntry.LocationType = LocationType;
9695  ActionVectorEntry.SequenceType = SequenceType;
9696  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
9697  ActionVectorEntry.Warning = Warning;
9698  if(FormatType == TimeCmd)
9699  {
9700  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
9701  {;
9702  }
9703  ActionVectorEntry.Command = Second;
9704  }
9705  else if(FormatType == TimeCmdHeadCode)
9706  {
9707  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
9708  {;
9709  }
9710  ActionVectorEntry.Command = Second;
9711  ActionVectorEntry.OtherHeadCode = Third;
9712  }
9713  else if(FormatType == FNSNonRepeatToShuttle)
9714  {
9715  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
9716  {;
9717  }
9718  ActionVectorEntry.Command = Second;
9719  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
9720  }
9721  else if(FormatType == FSHNewService)
9722  {
9723  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
9724  {;
9725  }
9726  ActionVectorEntry.Command = Second;
9727  ActionVectorEntry.OtherHeadCode = Third;
9728  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
9729  }
9730  else if(FormatType == ExitRailway)
9731  {
9732  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
9733  {;
9734  }
9735  ActionVectorEntry.Command = Second;
9736  ActionVectorEntry.ExitList = ExitList;
9737  }
9738  else if(FormatType == FinRemHere)
9739  {
9740  ActionVectorEntry.Command = Second;
9741  }
9742  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9743  }
9744  }
9745  else // repeat
9746  {
9747  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
9748  {
9749  Utilities->CallLogPop(786);
9750  // error messages given in SplitRepeat
9751  return false;
9752  }
9753  if(FinalCall)
9754  {
9755  TActionVectorEntry ActionVectorEntry;
9756  ActionVectorEntry.FormatType = Repeat;
9757  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
9758  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
9759  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
9760  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
9761  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
9762  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
9763  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
9764  }
9765  }
9766  }
9767  }
9768  if(FinalCall)
9769  {
9770  TrainDataVector.push_back(TempTrainDataEntry);
9771  }
9772  }
9773  Utilities->CallLogPop(80);
9774  return true;
9775 }
9776 
9777 // ---------------------------------------------------------------------------
9778 
9779 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
9780 {
9781  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
9782  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
9783  {
9784  Utilities->CallLogPop(1890);
9785  return false;
9786  }
9787  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
9788  {
9789  Utilities->CallLogPop(1891);
9790  return false;
9791  }
9792  Utilities->CallLogPop(1892);
9793  return true;
9794 }
9795 
9796 // ---------------------------------------------------------------------------
9797 
9798 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
9799  // 1st 5 chars must be HH:MM, anything else will be ignored
9800 {
9801  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
9802  if(TimeStr.Length() < 5)
9803  {
9804  Utilities->CallLogPop(926);
9805  return false;
9806  }
9807  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
9808  {
9809  Utilities->CallLogPop(927);
9810  return false;
9811  }
9812  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
9813  {
9814  Utilities->CallLogPop(928);
9815  return false;
9816  }
9817  if(TimeStr[3] != ':')
9818  {
9819  Utilities->CallLogPop(929);
9820  return false;
9821  }
9822  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
9823  {
9824  Utilities->CallLogPop(930);
9825  return false;
9826  }
9827  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
9828  {
9829  Utilities->CallLogPop(931);
9830  return false;
9831  }
9832  while(TimeStr.Length() > 5)
9833  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
9834  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
9835  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
9836 
9837  if((WholeHours + FracHour) >= 95.98334)
9838  {
9839  Utilities->CallLogPop(1817);
9840  return false; // > 95h 59m
9841  }
9842  Time = TDateTime((WholeHours + FracHour) / 24);
9843  Utilities->CallLogPop(932);
9844  return true;
9845 }
9846 
9847 // ---------------------------------------------------------------------------
9848 
9849 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
9850  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
9851  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
9852 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
9853  Return false for failure.
9854  See description above under ProcessOneTimetableLinefor details of train action entries
9855  NB all types set except LocationType for Sns as may be located or not
9856 */ {
9857  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
9858  Warning = false;
9859  TDateTime TempTime;
9860 
9861  if(OneEntry.Length() > 0)
9862  {
9863  if(OneEntry[1] == 'W') // warning
9864  {
9865  Warning = true;
9866  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
9867  // strip it off
9868  }
9869  }
9870  if(OneEntry == "Frh")
9871  {
9872  FormatType = FinRemHere;
9873  SequenceType = Finish;
9874  LocationType = AtLocation;
9875  ShuttleLinkType = NotAShuttleLink;
9876  Second = "Frh";
9877  Utilities->CallLogPop(1016);
9878  return true;
9879  }
9880  if(OneEntry.Length() < 7)
9881  {
9882  Utilities->CallLogPop(907);
9883  return false; // 'HH:MM;' + at least a one-letter location name
9884  }
9885  int Pos = OneEntry.Pos(';'); // first segment delimiter
9886 
9887  if(Pos != 6)
9888  {
9889  Utilities->CallLogPop(908);
9890  return false;
9891  // no delimiter or delimiter not in position 6, has to be a time so fail
9892  }
9893  First = OneEntry.SubString(1, 5); // has to be a time
9894  if(!CheckTimeValidity(16, First, TempTime))
9895  {
9896  Utilities->CallLogPop(909);
9897  return false;
9898  }
9899  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
9900 
9901  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
9902  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
9903  {
9904  if(Remainder.Length() < 7)
9905  {
9906  Utilities->CallLogPop(910);
9907  return false; // 'HH:MM;' + at least a one-letter location name
9908  }
9909  Pos = Remainder.Pos(';'); // second segment delimiter
9910  if(Pos == 0)
9911  {
9912  Utilities->CallLogPop(911);
9913  return false;
9914  // no delimiter, has to be one between departure time & location
9915  }
9916  Second = Remainder.SubString(1, 5); // has to be a time
9917  if(!CheckTimeValidity(15, Second, TempTime))
9918  {
9919  Utilities->CallLogPop(912);
9920  return false;
9921  }
9922  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9923  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
9924  {
9925  Utilities->CallLogPop(913);
9926  return false;
9927  }
9928  FormatType = TimeTimeLoc;
9929  SequenceType = Intermediate;
9930  LocationType = AtLocation;
9931  ShuttleLinkType = NotAShuttleLink;
9932  Utilities->CallLogPop(914);
9933  return true;
9934  }
9935  Pos = Remainder.Pos(';'); // second segment delimiter
9936  if(Pos == 0) // no third segment so second must be a location, or cdt
9937  {
9938  Second = Remainder;
9939  if(Second == "cdt")
9940  {
9941  FormatType = TimeCmd;
9942  ShuttleLinkType = NotAShuttleLink;
9943  LocationType = AtLocation;
9944  SequenceType = Intermediate;
9945  Utilities->CallLogPop(915);
9946  return true;
9947  }
9948  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
9949  {
9950  Utilities->CallLogPop(916);
9951  return false;
9952  }
9953  else
9954  {
9955  FormatType = TimeLoc;
9956  LocationType = AtLocation;
9957  SequenceType = Intermediate;
9958  ShuttleLinkType = NotAShuttleLink;
9959  Utilities->CallLogPop(917);
9960  return true;
9961  }
9962  }
9963  // here if second segment is a command, with a third & maybe fourth segments as details
9964  if((Pos != 4) && (Pos != 7) && (Pos != 8))
9965  {
9966  Utilities->CallLogPop(918);
9967  return false;
9968  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
9969  }
9970  Second = Remainder.SubString(1, Pos - 1); // command
9971 
9972  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9973  // details
9974  Pos = Remainder.Pos(';'); // third segment delimiter
9975  if(Pos == 0)
9976  Third = Remainder;
9977  else
9978  {
9979  Third = Remainder.SubString(1, Pos - 1);
9980  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
9981  }
9982  if((Second == "Snt") || (Second == "Snt-sh"))
9983  // third has to be 2 element idents with a space between
9984  {
9985  int SpacePos = Third.Pos(' ');
9986  if(SpacePos == 0)
9987  {
9988  Utilities->CallLogPop(919);
9989  return false; // no space
9990  }
9991  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
9992  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
9993  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
9994  if(CheckLocationsExistInRailway)
9995  {
9996  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
9997  {
9998  Utilities->CallLogPop(920);
9999  return false;
10000  }
10001  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
10002  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
10003  }
10004  if(Second == "Snt")
10005  {
10006  FormatType = StartNew;
10007  SequenceType = Start;
10008  LocationType = NoLocation;
10009  // can't be set until know whether located or not - done in SecondPassActions
10010  ShuttleLinkType = NotAShuttleLink;
10011  }
10012  else // Snt-sh
10013  {
10014  FormatType = SNTShuttle;
10015  LocationType = AtLocation;
10016  SequenceType = Start;
10017  ShuttleLinkType = ShuttleLink;
10018  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
10019  {
10020  Utilities->CallLogPop(1038);
10021  return false;
10022  }
10023  }
10024  Utilities->CallLogPop(921);
10025  return true;
10026  }
10027 
10028  if(Second == "Sns-sh") // third & fourth have to be headcodes
10029  {
10030  FormatType = SNSShuttle;
10031  LocationType = AtLocation;
10032  SequenceType = Start;
10033  ShuttleLinkType = ShuttleLink;
10034  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
10035  {
10036  Utilities->CallLogPop(1039);
10037  return false;
10038  }
10039  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
10040  {
10041  Utilities->CallLogPop(1040);
10042  return false;
10043  }
10044  Utilities->CallLogPop(1041);
10045  return true;
10046  }
10047 
10048  if(Second == "F-nshs")
10049  {
10050  FormatType = FNSNonRepeatToShuttle;
10051  LocationType = AtLocation;
10052  SequenceType = Finish;
10053  ShuttleLinkType = ShuttleLink;
10054  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
10055  {
10056  Utilities->CallLogPop(1047);
10057  return false;
10058  }
10059  Utilities->CallLogPop(1048);
10060  return true;
10061  }
10062 
10063  if(Second == "Sns-fsh")
10064  {
10065  FormatType = SNSNonRepeatFromShuttle;
10066  LocationType = AtLocation;
10067  SequenceType = Start;
10068  ShuttleLinkType = ShuttleLink;
10069  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
10070  {
10071  Utilities->CallLogPop(1098);
10072  return false;
10073  }
10074  Utilities->CallLogPop(1099);
10075  return true;
10076  }
10077 
10078  if(Second == "Fns-sh") // third & fourth have to be headcodes
10079  {
10080  FormatType = FSHNewService;
10081  LocationType = AtLocation;
10082  SequenceType = Finish;
10083  ShuttleLinkType = ShuttleLink;
10084  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
10085  {
10086  Utilities->CallLogPop(1050);
10087  return false;
10088  }
10089  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
10090  {
10091  Utilities->CallLogPop(1051);
10092  return false;
10093  }
10094  Utilities->CallLogPop(1052);
10095  return true;
10096  }
10097 
10098  // new segment for 'pas'
10099  if(Second == "pas") // third has to be a location
10100  {
10101  FormatType = PassTime;
10102  LocationType = EnRoute;
10103  SequenceType = Intermediate;
10104  ShuttleLinkType = NotAShuttleLink;
10105  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
10106  {
10107  Utilities->CallLogPop(1515);
10108  return false;
10109  }
10110  Utilities->CallLogPop(1516);
10111  return true;
10112  }
10113 
10114  // new segment for revised 'Fer'
10115  if(Second == "Fer")
10116  // third has to be a set of IDs separated by spaces, and at least 1
10117  {
10118  FormatType = ExitRailway;
10119  LocationType = EnRoute;
10120  SequenceType = Finish;
10121  ShuttleLinkType = NotAShuttleLink;
10122  if(CheckLocationsExistInRailway)
10123  {
10124  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
10125  {
10126  Utilities->CallLogPop(1519);
10127  return false;
10128  }
10129  }
10130  Utilities->CallLogPop(1520);
10131  return true;
10132  }
10133 
10134  // all remainder must be TimeCmdHeadCode types to be valid
10135  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
10136  (Second != "Frh-sh"))
10137  {
10138  Utilities->CallLogPop(922);
10139  return false; // all TimeCmdHeadCode types
10140  }
10141 
10142  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
10143  {
10144  Utilities->CallLogPop(923);
10145  return false;
10146  }
10147  FormatType = TimeCmdHeadCode;
10148  LocationType = AtLocation;
10149  if(Second == "Frh-sh")
10150  ShuttleLinkType = ShuttleLink;
10151  else
10152  ShuttleLinkType = NotAShuttleLink;
10153  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
10154  {
10155  SequenceType = Finish;
10156  }
10157  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
10158  {
10159  SequenceType = Intermediate;
10160  }
10161  if((Second == "Sfs") || (Second == "Sns"))
10162  {
10163  SequenceType = Start;
10164  }
10165  Utilities->CallLogPop(924);
10166  return true;
10167 }
10168 
10169 // ---------------------------------------------------------------------------
10170 
10171 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
10172 {
10173  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
10174  // and contains no special characters
10175  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
10176  if(LocStr == "")
10177  {
10178  Utilities->CallLogPop(1353);
10179  return false; // has to have at least one character
10180  }
10181  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
10182  {
10183  Utilities->CallLogPop(1354);
10184  return false; // can't begin with a number
10185  }
10186  for(int x = 1; x < LocStr.Length() + 1; x++)
10187  {
10188  if(LocStr[x] < ' ')
10189  {
10190  Utilities->CallLogPop(1355);
10191  return false; // contains a special character
10192  }
10193  if(LocStr[x] > 'z')
10194  {
10195  Utilities->CallLogPop(1356);
10196  return false; // contains a character outside the standard ASCII set
10197  }
10198  }
10199  // check exists in railway location list if CheckLocationsExistInRailway is true
10200  if(CheckLocationsExistInRailway)
10201  {
10202  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
10203  {
10204  TimetableMessage(GiveMessages, "Location name '" + LocStr +
10205  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
10206  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
10207  "that includes a continuation will not be valid.");
10208  Utilities->CallLogPop(1357);
10209  return false;
10210  }
10211  }
10212  Utilities->CallLogPop(1358);
10213  return true;
10214 }
10215 
10216 // ---------------------------------------------------------------------------
10217 
10218 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
10219 {
10220  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
10221  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
10222  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
10223  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
10224  HeadCode);
10225  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
10226  {
10227  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
10228  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Snt-sh, Sns-sh, Fns-sh or Frh-sh");
10229  Utilities->CallLogPop(1359);
10230  return false;
10231  }
10232  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
10233  for(int x = 1; x < (HeadCode.Length() + 1); x++)
10234  {
10235  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
10236  {
10237  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
10238  Utilities->CallLogPop(1895);
10239  return false;
10240  }
10241  }
10242  // secondly ensure the true Headcode only has letters or digits
10243  for(int x = 3; x >= 0; x--)
10244  {
10245  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
10246  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
10247  {
10248  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
10249  Utilities->CallLogPop(1790);
10250  return false;
10251  }
10252  }
10253  Utilities->CallLogPop(1364);
10254  return true;
10255 }
10256 
10257 // ---------------------------------------------------------------------------
10258 
10259 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
10260  // set of legitimate track element IDs, separated by spaces, and at least 1 present
10261 {
10262  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
10263  ExitList.clear();
10264  AnsiString CurrentID = "";
10265 
10266  if(IDSet.Length() == 0)
10267  {
10268  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
10269  Utilities->CallLogPop(1521);
10270  return false;
10271  }
10272  for(int x = 1; x <= IDSet.Length(); x++)
10273  {
10274  char C = IDSet[x];
10275  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
10276  {
10277  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
10278  Utilities->CallLogPop(1522);
10279  return false;
10280  }
10281  }
10282  int Pos = IDSet.Pos(' '); // look for the first space
10283 
10284  while(true)
10285  {
10286  if(Pos == 0)
10287  {
10288  CurrentID = IDSet;
10289  IDSet = "";
10290  }
10291  else
10292  {
10293  CurrentID = IDSet.SubString(1, Pos - 1);
10294  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
10295  }
10296  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
10297  if(VecPos == -1)
10298  {
10299  Utilities->CallLogPop(1523);
10300  return false; // messages given in GetTrackVectorPositionFromString
10301  }
10302  else
10303  {
10304  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
10305  {
10306  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
10307  Utilities->CallLogPop(1524);
10308  return false;
10309  }
10310  else
10311  {
10312  // first check for duplicates
10313  if(!ExitList.empty())
10314  {
10315  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
10316  {
10317  if(*ELIT == VecPos)
10318  {
10319  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
10320  Utilities->CallLogPop(1532);
10321  return false;
10322  }
10323  }
10324  }
10325  // of OK add it to the list
10326  ExitList.push_back(VecPos);
10327  }
10328  }
10329  if(IDSet == "")
10330  {
10331  Utilities->CallLogPop(1525);
10332  return true;
10333  }
10334  else
10335  {
10336  Pos = IDSet.Pos(' '); // look for the next space
10337  }
10338  } // while(true)
10339 }
10340 
10341 // ---------------------------------------------------------------------------
10342 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
10343  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
10344  // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
10345  // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
10346  // of each item
10347 {
10348  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
10349  int Pos = 0;
10350  AnsiString Remainder = "";
10351  int SemiColonCount = 0;
10352 
10353  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
10354  {
10355  if(TrainInfoStr[x] == ';')
10356  SemiColonCount++;
10357  }
10358  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
10359  {
10360  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
10361  "'. Should be headcode + optional description for a continuing service;" +
10362  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
10363  Utilities->CallLogPop(880);
10364  return false;
10365  }
10366  if(SemiColonCount == 0)
10367  {
10368  HeadCode = TrainInfoStr;
10369  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
10370  {
10371  Utilities->CallLogPop(881);
10372  return false;
10373  }
10374  Utilities->CallLogPop(882);
10375  return true;
10376  }
10377  if(SemiColonCount == 1) // headcode & description only
10378  {
10379  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
10380  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
10381  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
10382  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
10383  {
10384  Utilities->CallLogPop(883);
10385  return false;
10386  }
10387  if(Description == "")
10388  {
10389  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
10390  Utilities->CallLogPop(884);
10391  return false;
10392  }
10393  if(Description.Length() > 60)
10394  {
10395  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
10396  Utilities->CallLogPop(1157);
10397  return false;
10398  }
10399  for(int x = 1; x < Description.Length() + 1; x++)
10400  {
10401  if((Description[x] < ' ') || (Description[x] > '~'))
10402  {
10403  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
10404  Utilities->CallLogPop(885);
10405  return false;
10406  }
10407  }
10408  Utilities->CallLogPop(886);
10409  return true;
10410  }
10411  // if here must have 6 or 7 semicolons
10412  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
10413  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
10414  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
10415  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
10416  {
10417  Utilities->CallLogPop(887);
10418  return false;
10419  }
10420  Pos = Remainder.Pos(';'); // 2nd delimiter
10421  Description = Remainder.SubString(1, Pos - 1);
10422  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10423  if(Description == "")
10424  {
10425  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
10426  Utilities->CallLogPop(888);
10427  return false;
10428  }
10429  if(Description.Length() > 60)
10430  {
10431  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
10432  Utilities->CallLogPop(1158);
10433  return false;
10434  }
10435  for(int x = 1; x < Description.Length() + 1; x++)
10436  {
10437  if((Description[x] < ' ') || (Description[x] > 126))
10438  {
10439  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
10440  Utilities->CallLogPop(889);
10441  return false;
10442  }
10443  }
10444  Pos = Remainder.Pos(';'); // 3rd delimiter
10445  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
10446 
10447  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10448  if(StartSpeedStr == "")
10449  {
10450  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
10451  Utilities->CallLogPop(890);
10452  return false;
10453  }
10454  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
10455  {
10456  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
10457  {
10458  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
10459  Utilities->CallLogPop(891);
10460  return false;
10461  }
10462  }
10463  StartSpeed = StartSpeedStr.ToInt();
10464  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
10465  {
10466  StartSpeed = TTrain::MaximumSpeedLimit;
10467  if(!SSHigh) // added at v2.4.0
10468  {
10469  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10470  SSHigh = true;
10471  }
10472  }
10473  Pos = Remainder.Pos(';'); // 4th delimiter
10474  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
10475 
10476  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10477  if(MaxRunningSpeedStr == "")
10478  {
10479  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
10480  Utilities->CallLogPop(892);
10481  return false;
10482  }
10483  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
10484  {
10485  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
10486  {
10487  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
10488  Utilities->CallLogPop(893);
10489  return false;
10490  }
10491  }
10492  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
10493  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
10494  {
10495  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
10496  if(!MRSHigh) // added at v2.4.0
10497  {
10498  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10499  MRSHigh = true;
10500  }
10501  }
10502  if(MaxRunningSpeed < 10)
10503  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
10504  {
10505  MaxRunningSpeed = 10;
10506  if(!MRSLow) // added at v2.4.0
10507  {
10508  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
10509  MRSLow = true;
10510  }
10511  }
10512  Pos = Remainder.Pos(';'); // 5th delimiter
10513  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
10514 
10515  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10516  if(MassStr == "")
10517  {
10518  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
10519  Utilities->CallLogPop(895);
10520  return false;
10521  }
10522  for(int x = 1; x < MassStr.Length() + 1; x++)
10523  {
10524  if((MassStr[x] < '0') || (MassStr[x] > '9'))
10525  {
10526  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
10527  Utilities->CallLogPop(896);
10528  return false;
10529  }
10530  }
10531  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
10532  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
10533  {
10534  Mass = TTrain::MaximumMassLimit;
10535  if(!MassHigh) // added at v2.4.0
10536  {
10537  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
10538  MassHigh = true;
10539  }
10540  }
10541  if(Mass == 0)
10542  {
10543  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
10544  Utilities->CallLogPop(897);
10545  return false;
10546  }
10547  Pos = Remainder.Pos(';'); // 6th delimiter
10548  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
10549 
10550  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10551  if(MaxBrakeForceStr == "")
10552  {
10553  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
10554  Utilities->CallLogPop(898);
10555  return false;
10556  }
10557  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
10558  {
10559  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
10560  {
10561  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
10562  Utilities->CallLogPop(899);
10563  return false;
10564  }
10565  }
10566  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
10567 
10568  // convert to kg force
10569  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
10570  {
10571  MaxBrakeForce = Mass;
10572  if(!BFHigh) // added at v2.4.0
10573  {
10574  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
10575  BFHigh = true;
10576  }
10577  }
10578  if((MaxBrakeForce / Mass) < 0.01)
10579  {
10580  MaxBrakeForce = Mass * 0.01;
10581  if(!BFLow) // added at v2.4.0
10582  {
10583  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
10584  BFLow = true;
10585  }
10586  }
10587  // convert to m/s/s
10588  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
10589  // now may have just a power entry or power and signaller max. speed
10590  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
10591 
10592  if(SemiColonCount == 6)
10593  {
10594  GrossPowerStr = Remainder;
10595  SignallerSpeedStr = "30"; // default value
10596  }
10597  else // must be 7
10598  {
10599  Pos = Remainder.Pos(';'); // 7th delimiter
10600  GrossPowerStr = Remainder.SubString(1, Pos - 1);
10601  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10602  }
10603  // deal with GrossPower
10604  if(GrossPowerStr == "")
10605  {
10606  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
10607  Utilities->CallLogPop(901);
10608  return false;
10609  }
10610  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
10611  {
10612  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
10613  {
10614  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
10615  Utilities->CallLogPop(902);
10616  return false;
10617  }
10618  }
10619 
10620  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
10621 
10622  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
10623  {
10624  GrossPower = TTrain::MaximumPowerLimit;
10625  if(!PwrHigh)
10626  {
10627  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
10628  PwrHigh = true;
10629  }
10630  }
10631  else if(GrossPower == 0) // changed at v2.4.0
10632  {
10633  GrossPower = 0.1;
10634  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
10635  }
10636  else if((GrossPower > 0) && (GrossPower < 10000))
10637  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
10638  {
10639  GrossPower = 10000;
10640  }
10641  PowerAtRail = GrossPower * 0.8;
10642  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
10643 
10644  // deal with SignallerSpeed
10645  if(SignallerSpeedStr == "")
10646  {
10647  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
10648  Utilities->CallLogPop(1771);
10649  return false;
10650  }
10651  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
10652  {
10653  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
10654  {
10655  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
10656  Utilities->CallLogPop(1769);
10657  return false;
10658  }
10659  }
10660  SignallerSpeed = SignallerSpeedStr.ToInt();
10661  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
10662  {
10663  SignallerSpeed = TTrain::MaximumSpeedLimit;
10664  if(!SigSHigh)
10665  {
10666  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
10667  SigSHigh = true;
10668  }
10669  }
10670  if(SignallerSpeed < 10)
10671  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
10672  {
10673  SignallerSpeed = 10;
10674  if(!SigSLow)
10675  {
10676  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
10677  SigSLow = true;
10678  }
10679  // Utilities->CallLogPop(1770);
10680  // return false;
10681  }
10682  Utilities->CallLogPop(904);
10683  return true;
10684 }
10685 
10686 // ---------------------------------------------------------------------------
10687 
10688 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
10689  bool GiveMessages)
10690 {
10691  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
10692  // function checks validity of each item and returns false for error
10693  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
10694  if(OneEntry.Length() < 7)
10695  {
10696  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10697  Utilities->CallLogPop(865);
10698  return false;
10699  }
10700  int SemiColonCount = 0;
10701 
10702  for(int x = 1; x < OneEntry.Length() + 1; x++)
10703  {
10704  if(OneEntry[x] == ';')
10705  SemiColonCount++;
10706  }
10707  if(SemiColonCount != 3)
10708  {
10709  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10710  Utilities->CallLogPop(866);
10711  return false;
10712  }
10713  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
10714  {
10715  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
10716  Utilities->CallLogPop(867);
10717  return false;
10718  }
10719  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
10720  // strip off R;
10721 
10722  int Pos = 0;
10723 
10724  Pos = Remainder.Pos(';');
10725  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
10726 
10727  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10728  if(MinutesStr == "")
10729  {
10730  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
10731  Utilities->CallLogPop(868);
10732  return false;
10733  }
10734  if(MinutesStr.Length() > 3)
10735  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
10736  {
10737  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
10738  Utilities->CallLogPop(2119);
10739  return false;
10740  }
10741  for(int x = 1; x < MinutesStr.Length() + 1; x++)
10742  {
10743  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
10744  {
10745  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
10746  Utilities->CallLogPop(869);
10747  return false;
10748  }
10749  }
10750  RearStartOrRepeatMins = MinutesStr.ToInt();
10751  if(RearStartOrRepeatMins == 0)
10752  {
10753  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
10754  Utilities->CallLogPop(870);
10755  return false;
10756  }
10757  Pos = Remainder.Pos(';');
10758  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
10759 
10760  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
10761  if(DigitsStr == "")
10762  {
10763  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
10764  Utilities->CallLogPop(871);
10765  return false;
10766  }
10767  for(int x = 1; x < DigitsStr.Length() + 1; x++)
10768  {
10769  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
10770  {
10771  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
10772  Utilities->CallLogPop(872);
10773  return false;
10774  }
10775  }
10776  if(DigitsStr.Length() > 2)
10777  {
10778  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
10779  Utilities->CallLogPop(873);
10780  return false;
10781  }
10782  FrontStartOrRepeatDigits = DigitsStr.ToInt();
10783 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
10784  route rather than the service
10785  if(FrontStartOrRepeatDigits == 0)
10786  {
10787  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
10788  Utilities->CallLogPop(874);
10789  return false;
10790  }
10791 */
10792  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
10793  // new for v0.6b for unrestricted headcodes
10794  {
10795  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
10796  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
10797  Utilities->CallLogPop(1889);
10798  return false;
10799  }
10800  AnsiString NumberStr = Remainder;
10801 
10802  if(NumberStr == "")
10803  {
10804  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
10805  Utilities->CallLogPop(875);
10806  return false;
10807  }
10808  if(NumberStr.Length() > 4)
10809  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
10810  {
10811  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
10812  Utilities->CallLogPop(2118);
10813  return false;
10814  }
10815  for(int x = 1; x < NumberStr.Length() + 1; x++)
10816  {
10817  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
10818  // catches negative numbers
10819  {
10820  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
10821  Utilities->CallLogPop(876);
10822  return false;
10823  }
10824  }
10825  NumberOfRepeats = NumberStr.ToInt();
10826  if(NumberOfRepeats == 0)
10827  {
10828  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
10829  Utilities->CallLogPop(877);
10830  return false;
10831  }
10832  Utilities->CallLogPop(878);
10833  return true;
10834 }
10835 
10836 // ---------------------------------------------------------------------------
10837 
10838 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages)
10839 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
10840  vector rather than the timetable
10841  Note also that for unlocated Snt entries the LocationType hasn't yet been set
10842 
10843  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
10844 
10845  For info:-
10846  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
10847  {
10848  public:
10849  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled action entries, null
10851  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
10852  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
10853  int NumberOfRepeats; ///< the number of repeating services
10854  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
10856  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
10858  TExitList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
10859  TTimetableFormatType FormatType; ///< defines the timetable action type
10860  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
10861  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
10862  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
10863  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
10865  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
10867 
10868  // inline function
10869 
10871  TActionVectorEntry() {
10872  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
10873  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
10874  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
10875  Warning = false; SignallerControl = false;
10876  }
10877  };
10878 
10879  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
10880 
10881  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
10882  {
10883  public:
10884  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
10887  double MaxBrakeRate; ///< in metres/sec/sec
10888  double MaxRunningSpeed; ///< in km/h
10889  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
10890  int Mass; ///< in kg
10891  int NumberOfTrains; ///< number of repeats + 1
10892  int SignallerSpeed; ///< in km/h for use when under signaller control
10893  int StartSpeed; ///< in km/h
10894  TActionVector ActionVector; ///< all the actions for the train
10895  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
10896 
10897  //inline function
10898 
10900  TTrainDataEntry()
10901  {
10902  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
10903  }
10904  };
10905 
10906  Allowable successors:-
10907  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
10908  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
10909  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
10910  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10911  set location, else fails)
10912  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10913  set location, else fails)
10914  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10915  set location, else fails)
10916  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
10917  set location, else fails)
10918  Fns -> R only
10919  F-nshs -> Nothing (no repeats permitted)
10920  Fjo -> R only
10921  Frh -> R only
10922  Fer -> R only
10923  Frh-sh -> R only
10924  Fns-sh -> R only
10925  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
10926  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10927  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10928  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10929  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
10930  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10931  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10932  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
10933  Repeat -> Nothing
10934 
10935  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
10936  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
10937  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
10938  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
10939  Check all times increase or stay same through ActionVector
10940  Cycle through all entries in vector setting arr & dep times based on above list
10941  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
10942  Check locations match the arr & dep TimeLoc entries
10943  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
10944  Make above valid succession checks
10945  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
10946  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
10947  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
10948  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
10949  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
10950  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
10951  Set train info for Sfs & Sns entries
10952  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
10953  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
10954  element at each end, or length of 3 & 1 extra element at either end
10955  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
10956  Check all Cmds have EventTime set & Arr & Dep times = -1
10957  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
10958  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
10959 
10960  Give messages in function if errors detected and clear the vector. Return false for failure.
10961 */
10962 
10963 /* Earlier checks:-
10964  Checks carried out with error messages in this function:-
10965  At least one comma in the line (it's based on a csv file);
10966  No entries following train information;
10967  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10968  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10969  First entry not a start entry;
10970  Train information incomplete before a start entry;
10971  Entry follows a finish entry but doesn't begin with 'R';
10972  SplitEntry returns false in a finish entry - message repeats the entry for information;
10973  Last action entry isn't a finish entry.
10974 
10975  Function returns false with no message if:-
10976  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10977  time is found at all then an error message is given in the calling function);
10978  SplitTrainInfo returns false (message given in called function);
10979  SplitRepeat returns false (message given in called function).
10980 */ {
10981  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
10982  if(TrainDataVector.empty())
10983  {
10984  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
10985  TrainDataVector.clear();
10986  Utilities->CallLogPop(1832);
10987  return false;
10988  }
10989 
10990 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
10991  1) must have at least one actionvector entry
10992  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
10993  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
10994  4) first entry must be a start;
10995  4a) if first entry is Snt and not signallercontrol and second is a finish then it can only be Frh, Fjo or Fer//added for v2.0.0
10996  5) a start must be the first entry;
10997  6) a repeat entry must be the last;
10998  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
10999  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
11000  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
11001 */
11002 
11003  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
11004  {
11005  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11006  if(TrainDataVector.at(x).ActionVector.empty())
11007  {
11008  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
11009  TrainDataVector.clear();
11010  Utilities->CallLogPop(1833);
11011  return false;
11012  }
11013  }
11014  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
11015  {
11016  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11017  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11018  if(!(AVEntry0.SignallerControl))
11019  {
11020  if(TrainDataVector.at(x).ActionVector.size() == 1)
11021  {
11022  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
11023  TrainDataVector.clear();
11024  Utilities->CallLogPop(1822);
11025  return false;
11026  }
11027  }
11028  }
11029  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
11030  {
11031  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11032  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11033  if(AVEntry0.SignallerControl)
11034  {
11035  if(TrainDataVector.at(x).ActionVector.size() > 2)
11036  {
11037  SecondPassMessage(GiveMessages,
11038  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
11039  TDEntry.HeadCode);
11040  TrainDataVector.clear();
11041  Utilities->CallLogPop(1837);
11042  return false;
11043  }
11044  if(TrainDataVector.at(x).ActionVector.size() > 1)
11045  {
11046  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11047  if(AVEntry1.FormatType != Repeat)
11048  {
11049  SecondPassMessage(GiveMessages,
11050  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
11051  TrainDataVector.clear();
11052  Utilities->CallLogPop(1838);
11053  return false;
11054  }
11055  }
11056  }
11057  }
11058  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
11059  {
11060  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11061  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11062  if(AVEntry0.SequenceType != Start)
11063  {
11064  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
11065  TrainDataVector.clear();
11066  Utilities->CallLogPop(1824);
11067  return false;
11068  }
11069  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
11070  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
11071  // and others for a located Snt, but those checks done later
11072  {
11073  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11074  // must be a second entry if first not signallercontrol
11075  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
11076  {
11077  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
11078  TDEntry.HeadCode);
11079  TrainDataVector.clear();
11080  Utilities->CallLogPop(2046);
11081  return false;
11082  }
11083  }
11084  }
11085  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
11086  {
11087  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11088  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11089  {
11090  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11091  if((AVEntry.SequenceType == Start) && (y != 0))
11092  {
11093  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
11094  TrainDataVector.clear();
11095  Utilities->CallLogPop(1825);
11096  return false;
11097  }
11098  }
11099  }
11100  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
11101  {
11102  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11103  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11104  {
11105  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11106  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
11107  {
11108  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
11109  TrainDataVector.clear();
11110  Utilities->CallLogPop(1826);
11111  return false;
11112  }
11113  }
11114  }
11115  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
11116  {
11117  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11118  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11119  {
11120  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11121  if((y == 0) && AVEntry.SignallerControl)
11122  {
11123  break;
11124  }
11125  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
11126  {
11127  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
11128  {
11129  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
11130  TrainDataVector.clear();
11131  Utilities->CallLogPop(1827);
11132  return false;
11133  }
11134  if(AVEntry.FormatType == Repeat)
11135  {
11136  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
11137  if(LastAVEntry.SequenceType != Finish)
11138  {
11139  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
11140  TrainDataVector.clear();
11141  Utilities->CallLogPop(1828);
11142  return false;
11143  }
11144  }
11145  }
11146  }
11147  }
11148  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
11149  {
11150  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11151  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11152  {
11153  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11154  if(AVEntry.SequenceType == Finish)
11155  {
11156  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
11157  {
11158  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
11159  TrainDataVector.clear();
11160  Utilities->CallLogPop(1829);
11161  return false;
11162  }
11163  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
11164  {
11165  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11166  {
11167  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
11168  TDEntry.HeadCode);
11169  TrainDataVector.clear();
11170  Utilities->CallLogPop(1830);
11171  return false;
11172  }
11173  }
11174  }
11175  }
11176  }
11177 
11178  // end of new preliminary checks
11179 
11180  // check ActionVector present and check start event successor validity
11181  // For Snt & Snt-sh set location if stopped, don't set any times yet
11182  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11183  {
11184  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11185  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11186  // use reference so can change internals where necessary
11187  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
11188  {
11189  AnsiString LocationName = "";
11190  if(IsSNTEntryLocated(0, TDEntry, LocationName))
11191  // it is at a location
11192  {
11193  if(TDEntry.StartSpeed == 0) // stopped
11194  {
11195  AVEntry0.LocationName = LocationName;
11196  AVEntry0.LocationType = AtLocation;
11197  // check successor validity for located Snt that isn't a SignallerControl entry
11198  if(!AVEntry0.SignallerControl)
11199  {
11200  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11201  // at least 2 entries present checked in integrity check so (1) valid
11202  if(!AtLocSuccessor(AVEntry1))
11203  {
11204  // Frh following Snt-sh will return false in location check, so no need to check here
11205  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
11206  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11207  TrainDataVector.clear();
11208  Utilities->CallLogPop(523);
11209  return false;
11210  }
11211  }
11212  }
11213  else
11214  {
11215  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
11216  TDEntry.HeadCode);
11217  TrainDataVector.clear();
11218  Utilities->CallLogPop(791);
11219  return false;
11220  }
11221  }
11222  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
11223  {
11224  if(AVEntry0.Command == "Snt-sh")
11225  {
11226  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
11227  TrainDataVector.clear();
11228  Utilities->CallLogPop(1042);
11229  return false;
11230  }
11231  AVEntry0.LocationType = EnRoute;
11232  if(!AVEntry0.SignallerControl)
11233  {
11234  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11235  // at least 2 entries checked in integrity check so (1) valid
11236  if(!MovingSuccessor(AVEntry1))
11237  {
11238  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
11239  TDEntry.HeadCode);
11240  TrainDataVector.clear();
11241  Utilities->CallLogPop(790);
11242  return false;
11243  }
11244  }
11245  }
11246  }
11247  // check other start successors
11248  else if(AVEntry0.SequenceType == Start)
11249  {
11250  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
11251  // at least 2 entries present checked in integrity check so (1) valid
11252  if(!AtLocSuccessor(AVEntry1))
11253  {
11254  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
11255  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
11256  TrainDataVector.clear();
11257  Utilities->CallLogPop(793);
11258  return false;
11259  }
11260  }
11261  }
11262 
11263  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
11264  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11265  {
11266  bool FoundFlag = false;
11267  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11268  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
11269  // use reference so can change internals
11270  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
11271  {
11272  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
11273  {
11274  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
11275  if(AVEntry2.FormatType == TimeLoc)
11276  {
11277  FoundFlag = true;
11278  AVEntry.LocationName = AVEntry2.LocationName;
11279  break;
11280  }
11281  }
11282  if(!FoundFlag)
11283  {
11284  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
11285  TDEntry.HeadCode);
11286  TrainDataVector.clear();
11287  Utilities->CallLogPop(851);
11288  return false;
11289  }
11290  }
11291  }
11292 
11293  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
11294  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11295  {
11296  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11297  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11298  {
11299  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11300  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
11301  {
11302  if(AVEntry.LocationName == "")
11303  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
11304  {
11305  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
11306  ": an event should have had a location name associated with it but it could not be found");
11307  TrainDataVector.clear();
11308  Utilities->CallLogPop(1831);
11309  return false;
11310  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
11311  }
11312  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
11313  {
11314  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
11315  // use reference so can change internals where necessary
11316  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
11317  {
11318  AVEntry2.LocationName = AVEntry.LocationName;
11319  }
11320  else
11321  break;
11322  }
11323  }
11324  }
11325  }
11326  // all location names now set
11327 
11328  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
11329  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11330  {
11331  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11332  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11333  {
11334  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11335  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
11336  {
11337  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
11338  // i.e at least one more, must be a repeat
11339  {
11340  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
11341  {
11342  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
11343  TrainDataVector.clear();
11344  Utilities->CallLogPop(798);
11345  return false;
11346  }
11347  }
11348  }
11349  if(AVEntry.Command == "F-nshs")
11350  {
11351  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
11352  // i.e has to be the last
11353  {
11354  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
11355  TrainDataVector.clear();
11356  Utilities->CallLogPop(1049);
11357  return false;
11358  }
11359  }
11360  if(AVEntry.Command == "pas")
11361  {
11362  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11363  {
11364  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
11365  TrainDataVector.clear();
11366  Utilities->CallLogPop(1518);
11367  return false;
11368  }
11369  }
11370  if(AVEntry.Command == "jbo")
11371  {
11372  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11373  {
11374  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
11375  TrainDataVector.clear();
11376  Utilities->CallLogPop(800);
11377  return false;
11378  }
11379  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11380  if(!AtLocSuccessor(AVEntry2))
11381  {
11382  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
11383  ". The event isn't valid for a stationary train.");
11384  TrainDataVector.clear();
11385  Utilities->CallLogPop(801);
11386  return false;
11387  }
11388  }
11389  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
11390  {
11391  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11392  {
11393  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
11394  TrainDataVector.clear();
11395  Utilities->CallLogPop(802);
11396  return false;
11397  }
11398  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11399  if(!AtLocSuccessor(AVEntry2))
11400  {
11401  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
11402  ". The event isn't valid for a stationary train.");
11403  TrainDataVector.clear();
11404  Utilities->CallLogPop(803);
11405  return false;
11406  }
11407  }
11408  if(AVEntry.Command == "cdt")
11409  {
11410  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11411  {
11412  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
11413  TrainDataVector.clear();
11414  Utilities->CallLogPop(804);
11415  return false;
11416  }
11417  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11418  if(!AtLocSuccessor(AVEntry2))
11419  {
11420  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
11421  ". The event isn't valid for a stationary train.");
11422  TrainDataVector.clear();
11423  Utilities->CallLogPop(805);
11424  return false;
11425  }
11426  }
11427  if(AVEntry.FormatType == TimeTimeLoc)
11428  {
11429  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11430  {
11431  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
11432  TrainDataVector.clear();
11433  Utilities->CallLogPop(806);
11434  return false;
11435  }
11436  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11437  if(!MovingSuccessor(AVEntry2))
11438  {
11439  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
11440  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
11441  TrainDataVector.clear();
11442  Utilities->CallLogPop(807);
11443  return false;
11444  }
11445  }
11446  if(AVEntry.FormatType == PassTime)
11447  {
11448  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11449  {
11450  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
11451  TrainDataVector.clear();
11452  Utilities->CallLogPop(1530);
11453  return false;
11454  }
11455  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11456  if(!MovingSuccessor(AVEntry2))
11457  {
11458  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
11459  ". The event isn't valid for a moving train.");
11460  TrainDataVector.clear();
11461  Utilities->CallLogPop(1531);
11462  return false;
11463  }
11464  }
11465  if(AVEntry.FormatType == Repeat)
11466  {
11467  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
11468  {
11469  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
11470  TrainDataVector.clear();
11471  Utilities->CallLogPop(808);
11472  return false;
11473  }
11474  }
11475  }
11476  }
11477 
11478  // set arrival & departure times for TimeLocs & set their EventTimes to -1
11479  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11480  {
11481  bool LastEntryIsAnArrival = false;
11482  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
11483  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
11484  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
11485  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
11486  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
11487  {
11488  LastEntryIsAnArrival = false;
11489  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11490  {
11491  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11492  if(AVEntry.FormatType == TimeLoc)
11493  {
11494  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
11495  {
11496  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
11497  }
11498  if(LastEntryIsAnArrival)
11499  {
11500  AVEntry.DepartureTime = AVEntry.EventTime;
11501  AVEntry.EventTime = TDateTime(-1);
11502  LastEntryIsAnArrival = false;
11503  }
11504  else // last entry a departure
11505  {
11506  AVEntry.ArrivalTime = AVEntry.EventTime;
11507  AVEntry.EventTime = TDateTime(-1);
11508  LastEntryIsAnArrival = true;
11509  }
11510  }
11511  }
11512  }
11513  else // all others stopped at beginning
11514  {
11515  LastEntryIsAnArrival = true;
11516  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11517  {
11518  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11519  if(AVEntry.FormatType == TimeLoc)
11520  {
11521  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
11522  {
11523  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
11524  }
11525  if(LastEntryIsAnArrival)
11526  {
11527  AVEntry.DepartureTime = AVEntry.EventTime;
11528  AVEntry.EventTime = TDateTime(-1);
11529  LastEntryIsAnArrival = false;
11530  }
11531  else // last entry a departure
11532  {
11533  AVEntry.ArrivalTime = AVEntry.EventTime;
11534  AVEntry.EventTime = TDateTime(-1);
11535  LastEntryIsAnArrival = true;
11536  }
11537  }
11538  }
11539  }
11540  }
11541  // perform remaining successor checks for TimeLocs
11542  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11543  {
11544  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11545  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11546  {
11547  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11548  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
11549  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
11550  {
11551  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11552  {
11553  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
11554  TrainDataVector.clear();
11555  Utilities->CallLogPop(809);
11556  return false;
11557  }
11558  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11559  if(!AtLocSuccessor(AVEntry2))
11560  {
11561  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
11562  ". The event isn't valid for a stationary train.");
11563  TrainDataVector.clear();
11564  Utilities->CallLogPop(810);
11565  return false;
11566  }
11567  }
11568  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
11569  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
11570  {
11571  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
11572  {
11573  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
11574  TrainDataVector.clear();
11575  Utilities->CallLogPop(811);
11576  return false;
11577  }
11578  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
11579  if(!MovingSuccessor(AVEntry2))
11580  {
11581  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
11582  ". The event isn't valid for a moving train.");
11583  TrainDataVector.clear();
11584  Utilities->CallLogPop(812);
11585  return false;
11586  }
11587  }
11588  }
11589  }
11590 
11591  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
11592  // & repeats have no times set
11593  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11594  {
11595  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11596  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11597  {
11598  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11599  if(AVEntry.FormatType == TimeLoc)
11600  {
11601  if(AVEntry.EventTime != TDateTime(-1))
11602  {
11603  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
11604  }
11605  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
11606  {
11607  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
11608  }
11609  }
11610  if(AVEntry.FormatType == TimeTimeLoc)
11611  {
11612  if(AVEntry.EventTime != TDateTime(-1))
11613  {
11614  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
11615  }
11616  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
11617  {
11618  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
11619  }
11620  }
11621  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
11622  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
11623  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
11624  {
11625  if(AVEntry.EventTime == TDateTime(-1))
11626  {
11627  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
11628  }
11629  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
11630  {
11631  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
11632  }
11633  }
11634  if(AVEntry.FormatType == Repeat)
11635  {
11636  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
11637  {
11638  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
11639  }
11640  }
11641  }
11642  }
11643 
11644  // check times stay same or increase, note that can have time of 0 if include midnight
11645  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11646  {
11647  TDateTime CurrentTime = TTClockTime; // the timetable start time
11648  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11649  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11650  {
11651  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11652  if(AVEntry.FormatType == Repeat)
11653  break;
11654  if(AVEntry.FormatType == FinRemHere)
11655  break;
11656  if(AVEntry.FormatType == TimeTimeLoc)
11657  {
11658  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
11659  {
11660  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
11661  TDEntry.HeadCode);
11662  TrainDataVector.clear();
11663  Utilities->CallLogPop(813);
11664  return false;
11665  }
11666  if(AVEntry.ArrivalTime < CurrentTime)
11667  {
11668  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
11669  TDEntry.HeadCode);
11670  TrainDataVector.clear();
11671  Utilities->CallLogPop(814);
11672  return false;
11673  }
11674  CurrentTime = AVEntry.DepartureTime;
11675  continue;
11676  }
11677  if(AVEntry.FormatType == TimeLoc)
11678  {
11679  if(AVEntry.ArrivalTime >= TDateTime(0))
11680  {
11681  if(AVEntry.ArrivalTime < CurrentTime)
11682  {
11683  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
11684  TrainDataVector.clear();
11685  Utilities->CallLogPop(815);
11686  return false;
11687  }
11688  CurrentTime = AVEntry.ArrivalTime;
11689  }
11690  else
11691  {
11692  if(AVEntry.DepartureTime < CurrentTime)
11693  // both may be 0 legitimately so must allow for this
11694  {
11695  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
11696  TrainDataVector.clear();
11697  Utilities->CallLogPop(816);
11698  return false;
11699  }
11700  CurrentTime = AVEntry.DepartureTime;
11701  }
11702  continue;
11703  }
11704  if(AVEntry.EventTime < CurrentTime)
11705  // all others have EventTime set
11706  {
11707  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
11708  ", may be before timetable start time");
11709  TrainDataVector.clear();
11710  Utilities->CallLogPop(835);
11711  return false;
11712  }
11713  CurrentTime = AVEntry.EventTime;
11714  continue;
11715  }
11716  }
11717 
11718  // check locations consistent
11719  AnsiString LastLocationName = "";
11720 
11721  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11722  {
11723  bool LastEntryIsAnArrival = false;
11724  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11725  // first deal with moving Snt entries (all else stopped)
11726  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
11727  {
11728  LastEntryIsAnArrival = false;
11729  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
11730  if(LastLocationName != "")
11731  {
11732  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
11733  }
11734  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
11735  y++) // note that immediate successor to a moving Snt can only be a Moving type
11736  // if it's a SignallerControl entry then the condition isn't met
11737  {
11738  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11739  if(AVEntry.FormatType == Repeat)
11740  break; // repeat = reached end (+allows repeat after signaller controlled entry)
11741  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
11742  {
11743  if(AVEntry.LocationName != LastLocationName)
11744  {
11745  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11746  AVEntry.Command);
11747  TrainDataVector.clear();
11748  Utilities->CallLogPop(823);
11749  return false;
11750  }
11751  }
11752  else if(AVEntry.FormatType == TimeCmd)
11753  // cdt is the only TimeCmd
11754  {
11755  if(AVEntry.LocationName != LastLocationName)
11756  {
11757  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11758  AVEntry.Command);
11759  TrainDataVector.clear();
11760  Utilities->CallLogPop(824);
11761  return false;
11762  }
11763  }
11764  else if(AVEntry.FormatType == TimeTimeLoc)
11765  {
11766  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11767  // last entry must be a departure or would have failed earlier
11768  {
11769  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11771 // SecondPassMessage(GiveMessages,
11772 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
11773 // TrainDataVector.clear();
11774 // Utilities->CallLogPop(825);
11775 // return false;
11776  }
11777  LastLocationName = AVEntry.LocationName;
11778  LastEntryIsAnArrival = false;
11779  }
11780  else if(AVEntry.FormatType == TimeLoc)
11781  {
11782  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
11783  {
11784  SecondPassMessage(GiveMessages,
11785  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
11786  TrainDataVector.clear();
11787  Utilities->CallLogPop(826);
11788  return false;
11789  }
11790  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
11791  {
11792  SecondPassMessage(GiveMessages,
11793  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
11794  TrainDataVector.clear();
11795  Utilities->CallLogPop(827);
11796  return false;
11797  }
11798  LastLocationName = AVEntry.LocationName;
11799  LastEntryIsAnArrival = !LastEntryIsAnArrival;
11800  }
11801  }
11802  }
11803  else // all stationary starting entries
11804  {
11805  LastEntryIsAnArrival = true;
11806  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
11807  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11808  {
11809  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11810  if(AVEntry.FormatType == Repeat)
11811  break;
11812  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
11813  // no need to add anything for shuttle starts since they are at loc (0) anyway
11814  {
11815  if(AVEntry.LocationName != LastLocationName)
11816  {
11817  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11818  AVEntry.Command);
11819  TrainDataVector.clear();
11820  Utilities->CallLogPop(828);
11821  return false;
11822  }
11823  }
11824  else if(AVEntry.FormatType == TimeCmd)
11825  // cdt is the only TimeCmd
11826  {
11827  if(AVEntry.LocationName != LastLocationName)
11828  {
11829  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
11830  AVEntry.Command);
11831  TrainDataVector.clear();
11832  Utilities->CallLogPop(829);
11833  return false;
11834  }
11835  }
11836  else if(AVEntry.FormatType == TimeTimeLoc)
11837  {
11838  if((AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11839  // last entry must be a departure or would have failed earlier
11840  {
11841  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11843 // SecondPassMessage(GiveMessages,
11844 // "Error in timetable - a location event in a timed arrival and departure is the same as the last location for: " + TDEntry.HeadCode);
11845 // TrainDataVector.clear();
11846 // Utilities->CallLogPop(830);
11847 // return false;
11848  }
11849  LastLocationName = AVEntry.LocationName;
11850  LastEntryIsAnArrival = false;
11851  }
11852  else if(AVEntry.FormatType == TimeLoc)
11853  {
11854  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
11855  {
11856  SecondPassMessage(GiveMessages,
11857  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
11858  TrainDataVector.clear();
11859  Utilities->CallLogPop(831);
11860  return false;
11861  }
11862  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
11863  {
11864  SecondPassMessage(GiveMessages,
11865  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
11867 // TrainDataVector.clear();
11868 // Utilities->CallLogPop(832);
11869 // return false;
11870  }
11871  LastLocationName = AVEntry.LocationName;
11872  LastEntryIsAnArrival = !LastEntryIsAnArrival;
11873  }
11874  }
11875  }
11876  }
11877 
11878  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
11879  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
11880  AnsiString LocationNameToBeChecked = "";
11881 
11882  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11883  {
11884  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
11885  unsigned int y = 0;
11886  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
11887  // first discard unlocated Snt entries as they don't have location name set
11888  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
11889  {
11890  y = 1;
11891  }
11892  while(y < TDEntry.ActionVector.size())
11893  // need to check each location name separately in turn, skipped for SignallerControl entries
11894  // if y == 1
11895  {
11896  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
11897  {
11898  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
11899  }
11900  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
11901  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
11902  {
11903  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
11904  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
11905  {
11906  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
11907  }
11908  if(AVEntry.Command == "cdt")
11909  {
11910  break; // out of the 'z' loop since the check is only valid up to a change of direction
11911  }
11912  if(AVEntry.LocationName == LocationNameToBeChecked)
11913  continue; // keep going while name same
11914  if(AVEntry.LocationName != LocationNameToBeChecked)
11915  // if name different check forwards to see if repeats
11916  {
11917  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
11918  {
11919  if(TDEntry.ActionVector.at(a).Command == "cdt")
11920  {
11921  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
11922  }
11923  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && !TwoOrMoreLocationsWarningGiven && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
11924  {
11925  ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
11927  // SecondPassMessage(GiveMessages, "Error in timetable - a location entry appears twice inappropriately for: " + TDEntry.HeadCode);
11928  // TrainDataVector.clear();
11929  // Utilities->CallLogPop(833);
11930  // return false;
11931  }
11932  }
11933  break; // out of the 'z' loop since have checked 'a' as far as need to
11934  }
11935  }
11936  y++;
11937  }
11938  }
11939 
11940  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
11941  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
11942  {
11943  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11944  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11945  {
11946  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11947  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
11948  {
11949  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
11950  }
11951  AnsiString LocName = "";
11952  // dummy, only used so can call IsSNTEntryLocated
11953  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
11954  {
11955  if(AVEntry.LocationName == "")
11956  {
11957  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
11958  }
11959  }
11960  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
11961  {
11962  if(AVEntry.LocationName != "")
11963  {
11964  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
11965  }
11966  }
11967  }
11968  }
11969 
11970 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
11971  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
11972  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
11973 
11974  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
11975  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
11976 
11977  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
11978  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
11979  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
11980 */
11981  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
11982  // non-shuttles & non-repeating links separately, but don't check that there isn't a
11983  // duplicate between a non-repeating shuttle and another - leave original tests in as
11984  // these also set the pointers
11985  {
11986  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
11987  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
11988  {
11989  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
11990  if(AVEntry.OtherHeadCode != "")
11991  {
11992  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
11993  {
11994  Utilities->CallLogPop(1584);
11995  return false; // error message given in called function
11996  }
11997  }
11998  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
11999  {
12000  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
12001  {
12002  Utilities->CallLogPop(1585);
12003  return false; // error message given in called function
12004  }
12005  }
12006  }
12007  }
12008 
12009  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12010  {
12011  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12012  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12013  {
12014  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12015  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12016  {
12017  if(AVEntry.OtherHeadCode != "")
12018  {
12019  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
12020  // false = non-shuttle
12021  {
12022  Utilities->CallLogPop(864);
12023  return false; // error message given in called function
12024  }
12025  }
12026  }
12027  }
12028  }
12029 
12030  // now repeat the check just for the shuttles
12031  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12032  {
12033  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12034  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12035  {
12036  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12037  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
12038  {
12039  if(AVEntry.OtherHeadCode != "")
12040  {
12041  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
12042  // true = shuttle
12043  {
12044  Utilities->CallLogPop(1100);
12045  return false; // error message given in called function
12046  }
12047  }
12048  }
12049  }
12050  }
12051 
12052  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
12053  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12054  {
12055  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12056  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12057  {
12058  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12059  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
12060  {
12062  {
12063  Utilities->CallLogPop(1060);
12064  return false; // error message given in called function
12065  }
12066  }
12067  }
12068  }
12069 
12070  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
12071  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
12072  // don't ever need to and as designed would skip repeats
12073  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12074  {
12075  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12076  {
12077  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12078  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
12079  {
12080  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
12081  {
12082  Utilities->CallLogPop(1090);
12083  return false; // error message given in called function
12084  }
12085  }
12086  }
12087  }
12088 
12089  // check all entries have all types set to something
12090  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12091  {
12092  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12093  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12094  {
12095  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12096  if(AVEntry.FormatType == NoFormat)
12097  {
12098  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
12099  }
12100  else if(AVEntry.SequenceType == NoSequence)
12101  {
12102  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
12103  }
12104  else if(AVEntry.LocationType == NoLocation)
12105  {
12106  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
12107  }
12108  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
12109  {
12110  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
12111  }
12112  }
12113  }
12114 
12115  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
12116  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12117  {
12118  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12119  // non-const reference so can alter content
12120  TTrainOperatingData TData;
12121  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
12122  if(LastAVEntry.FormatType == Repeat) // check if a repeat
12123  {
12124 /*
12125  class TTrainOperatingData
12126  {
12127  public:
12128  int TrainID; - default, set at construction
12129  TActionEventType EventReported; used during operation
12130  TRunningEntry RunningEntry; - default, set at construction
12131  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
12132  };
12133 */
12134  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
12135  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
12136  {
12137  TDEntry.TrainOperatingDataVector.push_back(TData);
12138  }
12139  }
12140  else
12141  TDEntry.NumberOfTrains = 1;
12142  }
12143 
12144  // check that don't include any Continuation names
12145  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12146  {
12147  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12148  {
12149  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
12150  AnsiString HC = TrainDataVector.at(x).HeadCode;
12151  if(LocName != "")
12152  {
12153  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
12154  {
12155  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
12156  TrainDataVector.clear();
12157  Utilities->CallLogPop(1578);
12158  return false;
12159  }
12160  }
12161  }
12162  }
12163 
12164  // check that all repeat times below 96h
12165  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12166  {
12167  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
12168  int IncMinutes = 0;
12169  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
12170  {
12171  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
12172  }
12173  else
12174  continue; // basic times already checked in CheckTimeValidity
12175  AnsiString HC = TrainDataVector.at(x).HeadCode;
12176  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12177  {
12178  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
12179  {
12180  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12181  {
12182  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
12183  TrainDataVector.clear();
12184  Utilities->CallLogPop(1818);
12185  return false;
12186  }
12187  }
12188  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
12189  {
12190  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12191  // 3d 23h 59m = 3.9993055556
12192  {
12193  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12194  TrainDataVector.clear();
12195  Utilities->CallLogPop(1819);
12196  return false;
12197  }
12198  }
12199  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
12200  {
12201  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
12202  // 3d 23h 59m = 3.9993055556
12203  {
12204  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
12205  TrainDataVector.clear();
12206  Utilities->CallLogPop(1820);
12207  return false;
12208  }
12209  }
12210  }
12211  }
12212 
12213  // Now that all set up change any extended headcodes back to ordinary headcodes
12214  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12215  {
12216  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
12217  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12218  {
12219  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
12220  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
12221  }
12222  }
12223 
12224  // SaveTrainDataVectorToFile(0);//test
12226  Utilities->CallLogPop(782);
12227  return true;
12228 }
12229 
12230 // ---------------------------------------------------------------------------
12231 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
12233 {
12234  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
12235 }
12236 
12237 // ---------------------------------------------------------------------------
12238 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
12240 {
12241  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
12242  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
12243  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
12244 }
12245 
12246 // ---------------------------------------------------------------------------
12247 
12248 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
12249 {
12250  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
12251  if(HeadCode.Length() > 4) // ignore otherwise
12252  {
12253  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
12254  }
12255  Utilities->CallLogPop(1593);
12256 }
12257 
12258 // ---------------------------------------------------------------------------
12259 
12260 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
12261 {
12262  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
12263  SecondHeadCode);
12264  int ForwardCount = 0;
12265  int ReverseCount = 0;
12266 
12267  if(MainHeadCode == SecondHeadCode)
12268  {
12269  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
12270  TrainDataVector.clear();
12271  Utilities->CallLogPop(1594);
12272  return false;
12273  }
12274 
12275  // forward check
12276  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12277  {
12278  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12279  if(TDEntry.HeadCode == MainHeadCode)
12280  {
12281  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12282  {
12283  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12284  if(AVEntry.OtherHeadCode == SecondHeadCode)
12285  {
12286  ForwardCount++;
12287  }
12288  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
12289  // need own check in case both 'Other' & 'NonRepeating' have same headcode
12290  {
12291  ForwardCount++;
12292  }
12293  }
12294  }
12295  }
12296  if(ForwardCount == 0)
12297  // this is an exception because the headcodes are selected in the same order as the forward check
12298  {
12299  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
12300  }
12301 
12302  if(ForwardCount > 2)
12303  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
12304  {
12305  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
12306  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
12307  TrainDataVector.clear();
12308  Utilities->CallLogPop(1587);
12309  return false;
12310  }
12311 
12312  // reverse check
12313  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12314  {
12315  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12316  if(TDEntry.HeadCode == SecondHeadCode)
12317  {
12318  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12319  {
12320  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12321  if(AVEntry.OtherHeadCode == MainHeadCode)
12322  {
12323  ReverseCount++;
12324  }
12325  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
12326  {
12327  ReverseCount++;
12328  }
12329  }
12330  }
12331  }
12332 
12333  if(ReverseCount == 0)
12334  {
12335  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
12336  TrainDataVector.clear();
12337  Utilities->CallLogPop(1588);
12338  return false;
12339  }
12340  if(ReverseCount > 2)
12341  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
12342  {
12343  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
12344  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
12345  TrainDataVector.clear();
12346  Utilities->CallLogPop(1589);
12347  return false;
12348  }
12349 
12350  if(ForwardCount != ReverseCount)
12351  {
12352  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
12353  " than the other way round");
12354  TrainDataVector.clear();
12355  Utilities->CallLogPop(1610);
12356  return false;
12357  }
12358 
12359  Utilities->CallLogPop(1590);
12360  return true;
12361 }
12362 
12363 // ---------------------------------------------------------------------------
12364 
12365 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
12366 /* Return false for no find or more than one find, check correct types of link
12367  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
12368  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
12369  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
12370  Then do the same in reverse.
12371  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
12372  if main is Fns other must be Sns; if main is jbo other must be Fjo.
12373  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
12374  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
12375  for Sfs & Sns services. Finally check the repeat entries if present are consistent
12376 
12377  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
12378  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
12379 
12380  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
12381 */
12382 
12383 {
12384  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
12385  int ForwardCount = 0;
12386  int ReverseCount = 0;
12387  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
12388  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
12389  TTrainDataEntry *MainTrainDataPtr = 0;
12390  TTrainDataEntry *OtherTrainDataPtr = 0;
12391 
12392  // forward check
12393  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12394  {
12395  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12396  if(TDEntry.HeadCode == MainHeadCode)
12397  {
12398  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12399  {
12400  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12401  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12402  {
12403  if(AVEntry.OtherHeadCode == OtherHeadCode)
12404  {
12405  MainTrainDataPtr = &TrainDataVector.at(x);
12406  ForwardEntryPtr = &AVEntry;
12407  ForwardCount++;
12408  ForwardTDVectorNumber = x;
12409  }
12410  }
12411  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
12412  (AVEntry.Command == "Frh-sh")))
12413  {
12414  if(AVEntry.OtherHeadCode == OtherHeadCode)
12415  {
12416  MainTrainDataPtr = &TrainDataVector.at(x);
12417  ForwardEntryPtr = &AVEntry;
12418  ForwardCount++;
12419  ForwardTDVectorNumber = x;
12420  }
12421  }
12422  }
12423  }
12424  }
12425  if(ForwardCount == 0)
12426  // this is an exception because the headcodes are selected in the same order as the forward check
12427  {
12428  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
12429  }
12430  if(ForwardCount > 1)
12431  {
12432  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
12433  MainHeadCode);
12434  TrainDataVector.clear();
12435  Utilities->CallLogPop(836);
12436  return false;
12437  }
12438 
12439  // reverse check
12440  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12441  {
12442  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12443  if(TDEntry.HeadCode == OtherHeadCode)
12444  {
12445  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12446  {
12447  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12448  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
12449  {
12450  if(AVEntry.OtherHeadCode == MainHeadCode)
12451  {
12452  OtherTrainDataPtr = &TrainDataVector.at(x);
12453  ReverseCount++;
12454  ReverseEntryPtr = &AVEntry;
12455  ReverseTDVectorNumber = x;
12456  }
12457  }
12458  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
12459  {
12460  if(AVEntry.OtherHeadCode == MainHeadCode)
12461  {
12462  OtherTrainDataPtr = &TrainDataVector.at(x);
12463  ReverseCount++;
12464  ReverseEntryPtr = &AVEntry;
12465  ReverseTDVectorNumber = x;
12466  }
12467  }
12468  }
12469  }
12470  }
12471 
12472  if(ReverseCount == 0)
12473  {
12474  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
12475  TrainDataVector.clear();
12476  Utilities->CallLogPop(837);
12477  return false;
12478  }
12479  if(ReverseCount > 1)
12480  {
12481  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
12482  OtherHeadCode);
12483  TrainDataVector.clear();
12484  Utilities->CallLogPop(838);
12485  return false;
12486  }
12487 
12488  // these will all be false for !Shuttle
12489  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
12490  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
12491  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
12492  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
12493 
12494  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
12495  {
12496  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
12497  TrainDataVector.clear();
12498  Utilities->CallLogPop(1058);
12499  return false;
12500  }
12501 
12502  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
12503  {
12504  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
12505  TrainDataVector.clear();
12506  Utilities->CallLogPop(1059);
12507  return false;
12508  }
12509 
12510  if(ForwardEntryPtr->LocationName == "")
12511  {
12512  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
12513  ". One or other service does not have a location set");
12514  TrainDataVector.clear();
12515  Utilities->CallLogPop(526);
12516  return false;
12517  }
12518 
12519  if(ReverseEntryPtr->LocationName == "")
12520  {
12521  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
12522  ". One or other service does not have a location set");
12523  TrainDataVector.clear();
12524  Utilities->CallLogPop(527);
12525  return false;
12526  }
12527 
12528  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
12529  {
12530  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
12531  " is at a different location to the referencing train " + MainHeadCode);
12532  TrainDataVector.clear();
12533  Utilities->CallLogPop(842);
12534  return false;
12535  }
12536 
12537  // ignore shuttle repeat links for first time check
12538  if(!Shuttle)
12539  {
12540  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
12541  {
12542  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
12543  " has a different event time to the referencing train " + MainHeadCode);
12544  TrainDataVector.clear();
12545  Utilities->CallLogPop(525);
12546  return false;
12547  }
12548  }
12549 
12550  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
12551  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
12552  if(ForwardShuttleStart && ReverseShuttleFinish)
12553  // Shuttle must be true if these are true
12554  {
12555  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
12556  {
12557  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
12558  " first repeat restart time not consistent with finish service " + OtherHeadCode);
12559  TrainDataVector.clear();
12560  Utilities->CallLogPop(1055);
12561  return false;
12562  }
12563  }
12564 
12565  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
12566  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
12567  {
12568  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12569  {
12570  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
12571  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12572  TrainDataVector.clear();
12573  Utilities->CallLogPop(528);
12574  return false;
12575  }
12576  }
12577 
12578  if(ReverseEntryPtr->Command == "Fjo")
12579  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
12580  {
12581  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12582  {
12583  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
12584  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12585  TrainDataVector.clear();
12586  Utilities->CallLogPop(862);
12587  return false;
12588  }
12589  }
12590 
12591  if(ReverseEntryPtr->Command == "Fns")
12592  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
12593  {
12594  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
12595  {
12596  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
12597  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
12598  TrainDataVector.clear();
12599  Utilities->CallLogPop(529);
12600  return false;
12601  }
12602  }
12603 
12604  if(ForwardEntryPtr->Command == "Sfs")
12605  {
12606  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
12607  {
12608  SecondPassMessage(GiveMessages,
12609  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
12610  MainHeadCode);
12611  TrainDataVector.clear();
12612  Utilities->CallLogPop(530);
12613  return false;
12614  }
12615  }
12616 
12617  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
12618  {
12619  if(ReverseEntryPtr->Command != "Sfs")
12620  {
12621  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
12622  MainHeadCode);
12623  TrainDataVector.clear();
12624  Utilities->CallLogPop(839);
12625  return false;
12626  }
12627  else
12628  {
12629  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
12630  {
12631  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
12632  TrainDataVector.clear();
12633  Utilities->CallLogPop(849);
12634  return false;
12635  }
12636  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
12637  {
12638  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
12639  TrainDataVector.clear();
12640  Utilities->CallLogPop(850);
12641  return false;
12642  }
12643  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
12644  {
12645  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
12646  TrainDataVector.clear();
12647  Utilities->CallLogPop(846);
12648  return false;
12649  }
12650  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12651  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12652  if(OtherTrainDataPtr->Description == "")
12653  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
12654  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
12655  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12656  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12657  }
12658  }
12659 
12660  if(ForwardEntryPtr->Command == "Sns")
12661  {
12662  if(ReverseEntryPtr->Command != "Fns")
12663  {
12664  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
12665  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
12666  TrainDataVector.clear();
12667  Utilities->CallLogPop(531);
12668  return false;
12669  }
12670  }
12671 
12672  if(ForwardEntryPtr->Command == "Fns")
12673  {
12674  if(ReverseEntryPtr->Command != "Sns")
12675  {
12676  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
12677  " and forms a new service with headcode " + OtherHeadCode);
12678  TrainDataVector.clear();
12679  Utilities->CallLogPop(840);
12680  return false;
12681  }
12682  else
12683  {
12684  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12685  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12686  if(OtherTrainDataPtr->Description == "")
12687  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
12688  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12689  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12690  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
12691  }
12692  }
12693  if(ForwardEntryPtr->Command == "jbo")
12694  {
12695  if(ReverseEntryPtr->Command != "Fjo")
12696  {
12697  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
12698  " and is joined by a train with headcode " + OtherHeadCode);
12699  TrainDataVector.clear();
12700  Utilities->CallLogPop(841);
12701  return false;
12702  }
12703  }
12704 
12705  if(ForwardEntryPtr->Command == "Fjo")
12706  {
12707  if(ReverseEntryPtr->Command != "jbo")
12708  {
12709  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
12710  " and joins a train with headcode " + OtherHeadCode);
12711  TrainDataVector.clear();
12712  Utilities->CallLogPop(532);
12713  return false;
12714  }
12715  else
12716  {
12717  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12718  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12719  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
12720  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
12721  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
12722  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
12723  }
12724  }
12725 
12726  if(ForwardShuttleStart)
12727  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
12728  {
12729  if(!ReverseShuttleFinish)
12730  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
12731  {
12732  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
12733  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
12734  TrainDataVector.clear();
12735  Utilities->CallLogPop(1056);
12736  return false;
12737  }
12738  }
12739 
12740  if(ReverseShuttleStart)
12741  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
12742  {
12743  if(!ForwardShuttleFinish)
12744  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
12745  {
12746  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
12747  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
12748  TrainDataVector.clear();
12749  Utilities->CallLogPop(1057);
12750  return false;
12751  }
12752  else
12753  {
12754  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
12755  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
12756 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
12757  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
12758 */
12759  }
12760  }
12761 
12762  // check repeat information consistent if present
12763  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
12764  // and those not accessed here
12765 
12766  // still need to check the non-repeating links and that they have no repeats - do that outside this function
12767  bool MainRepeat = false, OtherRepeat = false;
12768  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
12769 
12770  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
12771  {
12772  MainRepeat = true;
12773  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
12774  }
12775  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
12776  {
12777  OtherRepeat = true;
12778  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
12779  }
12780  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
12781  {
12782  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
12783  " and the associated train with headcode " + OtherHeadCode);
12784  TrainDataVector.clear();
12785  Utilities->CallLogPop(844);
12786  return false;
12787  }
12788  if(MainRepeat && OtherRepeat)
12789  {
12790  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
12791  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
12792  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
12793  {
12794  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
12795  " and the associated train with headcode " + OtherHeadCode);
12796  TrainDataVector.clear();
12797  Utilities->CallLogPop(845);
12798  return false;
12799  }
12800  }
12801  Utilities->CallLogPop(863);
12802  return true;
12803 }
12804 
12805 // ---------------------------------------------------------------------------
12806 
12807 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
12808  // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
12809 {
12810  // strip spaces from extreme ends of input
12811  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
12812  if(Input == "")
12813  {
12814  Utilities->CallLogPop(856);
12815  return;
12816  }
12817  while(Input[1] == ' ')
12818  {
12819  if(Input.Length() > 1)
12820  {
12821  Input = Input.SubString(2, Input.Length() - 1);
12822  }
12823  else
12824  {
12825  Input = "";
12826  Utilities->CallLogPop(857);
12827  return;
12828  }
12829  }
12830  if(Input == "")
12831  {
12832  Utilities->CallLogPop(858);
12833  return;
12834  }
12835  while(Input[Input.Length()] == ' ')
12836  {
12837  if(Input.Length() > 1)
12838  {
12839  Input = Input.SubString(1, Input.Length() - 1);
12840  }
12841  else
12842  {
12843  Input = "";
12844  Utilities->CallLogPop(859);
12845  return;
12846  }
12847  }
12848  // now strip spaces immediately after all commas and semicolons within the text
12849  AnsiString Output = "";
12850  bool DelimiterFound = false;
12851 
12852  for(int x = 1; x < Input.Length() + 1; x++)
12853  {
12854  if(DelimiterFound)
12855  {
12856  if(Input[x] == ' ')
12857  continue;
12858  }
12859  if((Input[x] != ',') && (Input[x] != ';'))
12860  {
12861  DelimiterFound = false;
12862  Output = Output + Input[x];
12863  }
12864  else
12865  {
12866  DelimiterFound = true;
12867  Output = Output + Input[x];
12868  }
12869  }
12870  if(Output == "")
12871  {
12872  Input = "";
12873  Utilities->CallLogPop(860);
12874  return;
12875  }
12876  // now strip spaces immediately before all commas and semicolons within the text
12877  Input = Output;
12878  Output = "";
12879  DelimiterFound = false;
12880  for(int x = Input.Length(); x > 0; x--)
12881  {
12882  if(DelimiterFound)
12883  {
12884  if(Input[x] == ' ')
12885  continue;
12886  }
12887  if((Input[x] != ',') && (Input[x] != ';'))
12888  {
12889  DelimiterFound = false;
12890  Output = AnsiString(Input[x]) + Output;
12891  }
12892  else
12893  {
12894  DelimiterFound = true;
12895  Output = AnsiString(Input[x]) + Output;
12896  }
12897  }
12898  Input = Output;
12899  Utilities->CallLogPop(861);
12900 }
12901 
12902 // ---------------------------------------------------------------------------
12903 
12904 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
12905  // checks if an Snt or Snt-sh entry followed (somewhere, not necessarily immediately) by a TimeLoc has the same LocationName
12906  // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
12907  // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
12908  // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
12909  // are done in this function, they must be done elsewhere.
12910 {
12911  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
12912  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
12913 
12914  LocationName = "";
12915  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
12916  {
12917  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
12918  }
12920  {
12921  Utilities->CallLogPop(852);
12922  return false;
12923  }
12924  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
12925  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
12926 
12927  if(LocRear != "")
12928  LocationName = LocRear;
12929  else
12930  LocationName = LocFront;
12931  if(LocationName == "")
12932  {
12933  Utilities->CallLogPop(1036);
12934  return false;
12935  }
12936  if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed == 0))
12937  {
12938  Utilities->CallLogPop(1773);
12939  return true;
12940  }
12941  else if((AVEntry0.SignallerControl) && (TDEntry.StartSpeed > 0))
12942  {
12943  LocationName = "";
12944  Utilities->CallLogPop(1784);
12945  return false;
12946  }
12947 
12948  // here if not a signaller start entry so must be at least one more entry
12949  const TActionVectorEntry &AVEntry1 = TDEntry.ActionVector.at(1);
12950 
12951  // has to be at least 2 AV entries to pass the > 1 comma test in the preliminary check
12952  if(((AVEntry1.Command == "Frh") || (AVEntry1.Command == "Fjo") || (AVEntry1.Command == "F-nshs") || (AVEntry1.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
12953  { //added F-nshs at v2.5.1 so can stay at a
12954  Utilities->CallLogPop(1037); //location until become a new shuttle service
12955  return true; //added Fns at same time as saw no reason to exclude
12956  }
12957  AnsiString TimeLocLocationName;
12958  bool FoundFlag = false;
12959 
12960  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
12961  {
12962  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
12963  if(AVEntry.FormatType == TimeLoc)
12964  {
12965  FoundFlag = true;
12966  TimeLocLocationName = AVEntry.LocationName;
12967  break;
12968  }
12969  }
12970  if(!FoundFlag)
12971  {
12972  Utilities->CallLogPop(853);
12973  return false;
12974  }
12975  if(TimeLocLocationName == LocationName)
12976  {
12977  Utilities->CallLogPop(854);
12978  return true;
12979  }
12980  Utilities->CallLogPop(855);
12981  return false;
12982 }
12983 
12984 // ---------------------------------------------------------------------------
12985 
12986 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
12987 {
12988  // checks that the new train start elements are valid - both exist & are connected, and that not
12989  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
12990  // & not starting with front on a continuation
12991  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
12992  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
12993 
12994  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
12995  if(RearPosition < 0)
12996  // error message given in GetTrackVectorPositionFromString
12997  {
12998  Utilities->CallLogPop(759);
12999  return false;
13000  }
13001  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
13002  if(FrontPosition < 0)
13003  // error message given in GetTrackVectorPositionFromString
13004  {
13005  Utilities->CallLogPop(760);
13006  return false;
13007  }
13008  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
13009  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
13010  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
13011 
13012  // check front & rear connected
13013  for(int x = 0; x < 4; x++)
13014  {
13015  if(RearTrackElement.Conn[x] == FrontPosition)
13016  {
13017  RearExitPos = x;
13018  break;
13019  }
13020  if(x == 3)
13021  {
13022  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
13023  Utilities->CallLogPop(762);
13024  return false;
13025  }
13026  }
13027  // check not starting with front on a continuation
13028  if(FrontType == Continuation)
13029  {
13030  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
13031  Utilities->CallLogPop(937);
13032  return false;
13033  }
13034 
13035  // check not starting on a level crossing
13036  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
13037  {
13038  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
13039  Utilities->CallLogPop(1951);
13040  return false;
13041  }
13042  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
13043  {
13044  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
13045  Utilities->CallLogPop(1952);
13046  return false;
13047  }
13048 
13049  // check if trying to start on diverging leg of points
13050  if((RearType == Points) && (RearExitPos == 3))
13051  {
13052  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
13053  Utilities->CallLogPop(936);
13054  return false;
13055  }
13056 
13057  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
13058  {
13059  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
13060  Utilities->CallLogPop(1808);
13061  return false;
13062  }
13063  Utilities->CallLogPop(905);
13064  return true;
13065 }
13066 
13067 // ---------------------------------------------------------------------------
13068 
13069 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
13070  // Rear & front element validity already checked in CheckStartPositionValidity
13071  // This checks for points in correct orientation, no train at start position and not starting on a locked route
13072 {
13073  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
13074  AnsiString(RearExitPos));
13075  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
13076 
13077  if(RearTrackElement.TrackType == Continuation)
13078  EventType = FailTrainEntry;
13079  else
13080  EventType = FailCreateTrain;
13081  int FrontPosition = RearTrackElement.Conn[RearExitPos];
13082  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
13083  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
13084  TTrackType RearType = RearTrackElement.TrackType;
13085  TTrackType FrontType = FrontTrackElement.TrackType;
13086  AnsiString RearName, FrontName;
13087 
13088  if(RearTrackElement.ActiveTrackElementName != "")
13089  RearName = RearTrackElement.ActiveTrackElementName;
13090  else
13091  RearName = RearTrackElement.ElementID;
13092  if(FrontTrackElement.ActiveTrackElementName != "")
13093  FrontName = FrontTrackElement.ActiveTrackElementName;
13094  else
13095  FrontName = FrontTrackElement.ElementID;
13096 
13097  TPrefDirElement PrefDirElement; // needed for next function but not used
13098  int LockedVectorNumber; // needed for next function but not used
13099 
13100  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
13101  {
13102  if(ReportFlag)
13103  {
13104  if(EventType == FailCreateTrain)
13105  EventType = FailCreateLockedRoute;
13106  else
13107  EventType = FailEnterLockedRoute;
13108  LogActionError(47, HeadCode, "", EventType, FrontName);
13109  }
13110  Utilities->CallLogPop(940);
13111  return false;
13112  }
13113 
13114  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
13115  {
13116  if(ReportFlag)
13117  {
13118  if(EventType == FailCreateTrain)
13119  EventType = FailCreateLockedRoute;
13120  else
13121  EventType = FailEnterLockedRoute;
13122  LogActionError(48, HeadCode, "", EventType, RearName);
13123  }
13124  Utilities->CallLogPop(1809);
13125  return false;
13126  }
13127 
13128  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
13129  {
13130  if(ReportFlag)
13131  {
13132  LogActionError(27, HeadCode, "", EventType, RearName);
13133  }
13134  Utilities->CallLogPop(1810);
13135  return false;
13136  }
13137 
13138  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
13139  {
13140  if(ReportFlag)
13141  {
13142  if(EventType == FailCreateTrain)
13143  LogActionError(28, HeadCode, "", EventType, FrontName);
13144  else
13145  LogActionError(43, HeadCode, "", EventType, RearName);
13146  }
13147  Utilities->CallLogPop(941);
13148  return false;
13149  }
13150  if(RearType == Bridge)
13151  {
13152  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13153  {
13154  if(ReportFlag)
13155  {
13156  LogActionError(29, HeadCode, "", EventType, RearName);
13157  }
13158  Utilities->CallLogPop(942);
13159  return false;
13160  }
13161  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13162  {
13163  if(ReportFlag)
13164  {
13165  LogActionError(30, HeadCode, "", EventType, RearName);
13166  }
13167  Utilities->CallLogPop(943);
13168  return false;
13169  }
13170  }
13171  if(FrontType == Bridge)
13172  {
13173  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
13174  {
13175  if(ReportFlag)
13176  {
13177  if(EventType == FailCreateTrain)
13178  LogActionError(31, HeadCode, "", EventType, FrontName);
13179  else
13180  LogActionError(44, HeadCode, "", EventType, RearName);
13181  }
13182  Utilities->CallLogPop(944);
13183  return false;
13184  }
13185  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
13186  {
13187  if(ReportFlag)
13188  {
13189  if(EventType == FailCreateTrain)
13190  LogActionError(45, HeadCode, "", EventType, FrontName);
13191  else
13192  LogActionError(46, HeadCode, "", EventType, RearName);
13193  }
13194  Utilities->CallLogPop(945);
13195  return false;
13196  }
13197  }
13198 
13199  EventType = FailCreatePoints;
13200  if(RearType == Points)
13201  {
13202  if(RearTrackElement.Attribute == 1)
13203  {
13204  if(ReportFlag)
13205  {
13206  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
13207  }
13208  Utilities->CallLogPop(933);
13209  return false;
13210  }
13211  }
13212  if(FrontType == Points)
13213  {
13214  if(FrontTrackElement.Attribute == 1)
13215  {
13216  if(ReportFlag)
13217  {
13218  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
13219  }
13220  Utilities->CallLogPop(934);
13221  return false;
13222  }
13223  }
13224  Utilities->CallLogPop(939);
13225  return true;
13226 }
13227 
13228 // ---------------------------------------------------------------------------
13229 
13230 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
13231 {
13232  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
13233  "," + AnsiString(IncDigits));
13234  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
13235  {
13236  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
13237  }
13238  if(!Last2CharactersBothDigits(2, BaseHeadCode))
13239  {
13240  Utilities->CallLogPop(1893);
13241  return BaseHeadCode;
13242  }
13243  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
13244  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
13245 
13246  while(NextRepeatDigits >= 100)
13247  {
13248  NextRepeatDigits -= 100; // rolls over after 99
13249  }
13250  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
13251 
13252  if(NextRepeatDigitsStr.Length() < 2)
13253  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
13254  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
13255 
13256  Utilities->CallLogPop(1365);
13257  return NextRepeatHeadCode;
13258 }
13259 
13260 // ---------------------------------------------------------------------------
13261 
13262 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
13263 {
13264  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
13265  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
13266  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
13267 
13268  Utilities->CallLogPop(1366);
13269  return NextRepeatTime;
13270 }
13271 
13272 // ---------------------------------------------------------------------------
13273 
13274 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
13275  // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
13276 {
13277  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
13278  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
13279  int ForwardSecs = int(double(ForwardEventTime) * 86400);
13280  int ReverseSecs = int(double(ReverseEventTime) * 86400);
13281  int RepeatSecs = RepeatMinutes * 60;
13282 
13283  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
13284  {
13285  Utilities->CallLogPop(1367);
13286  return false;
13287  }
13288  else
13289  {
13290  Utilities->CallLogPop(1368);
13291  return true;
13292  }
13293 }
13294 
13295 // ---------------------------------------------------------------------------
13296 
13297 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
13298  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13299 {
13300  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
13301  NonRepeatingHeadCode);
13302  int ForwardCount = 0;
13303  int ReverseCount = 0;
13304  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13305  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13306  // Forward corresponds to Main, Reverse to Other
13307  TTrainDataEntry *MainTrainDataPtr = 0;
13308  TTrainDataEntry *OtherTrainDataPtr = 0;
13309 
13310  // forward check
13311  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13312  {
13313  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13314  if(TDEntry.HeadCode == MainHeadCode)
13315  {
13316  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13317  {
13318  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13319  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
13320  {
13321  MainTrainDataPtr = &TrainDataVector.at(x);
13322  ForwardEntryPtr = &AVEntry;
13323  ForwardCount++;
13324  ForwardTDVectorNumber = x;
13325  }
13326  }
13327  }
13328  }
13329  if(ForwardCount == 0)
13330  // this is an exception because the headcodes are selected in the same order as the forward check
13331  {
13332  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
13333  }
13334  if(ForwardCount > 1)
13335  {
13336  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
13337  MainHeadCode);
13338  TrainDataVector.clear();
13339  Utilities->CallLogPop(1061);
13340  return false;
13341  }
13342 
13343  // reverse check
13344  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13345  {
13346  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13347  if(TDEntry.HeadCode == NonRepeatingHeadCode)
13348  {
13349  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13350  {
13351  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13352  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13353  {
13354  OtherTrainDataPtr = &TrainDataVector.at(x);
13355  ReverseCount++;
13356  ReverseEntryPtr = &AVEntry;
13357  ReverseTDVectorNumber = x;
13358  }
13359  }
13360  }
13361  }
13362 
13363  if(ReverseCount == 0)
13364  {
13365  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
13366  TrainDataVector.clear();
13367  Utilities->CallLogPop(1062);
13368  return false;
13369  }
13370  if(ReverseCount > 1)
13371  {
13372  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13373  NonRepeatingHeadCode);
13374  TrainDataVector.clear();
13375  Utilities->CallLogPop(1063);
13376  return false;
13377  }
13378 
13379  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
13380  {
13381  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
13382  TrainDataVector.clear();
13383  Utilities->CallLogPop(1064);
13384  return false;
13385  }
13386 
13387  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
13388  {
13389  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
13390  TrainDataVector.clear();
13391  Utilities->CallLogPop(1065);
13392  return false;
13393  }
13394 
13395  if(ForwardEntryPtr->LocationName == "")
13396  {
13397  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
13398  ". One or other service does not have a location set");
13399  TrainDataVector.clear();
13400  Utilities->CallLogPop(1066);
13401  return false;
13402  }
13403 
13404  if(ReverseEntryPtr->LocationName == "")
13405  {
13406  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
13407  ". One or other service does not have a location set");
13408  TrainDataVector.clear();
13409  Utilities->CallLogPop(1067);
13410  return false;
13411  }
13412 
13413  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13414  {
13415  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
13416  " is at a different location to the referencing train " + MainHeadCode);
13417  TrainDataVector.clear();
13418  Utilities->CallLogPop(1068);
13419  return false;
13420  }
13421 
13422  if(ForwardEntryPtr->Command == "F-nshs")
13423  // i.e. the non repeating link into the shuttle service
13424  {
13425  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13426  {
13427  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
13428  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
13429  TrainDataVector.clear();
13430  Utilities->CallLogPop(1069);
13431  return false;
13432  }
13433  }
13434 
13435  if(ForwardEntryPtr->Command == "Fns-sh")
13436  // i.e. the non repeating link out from the shuttle service
13437  {
13438  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
13439  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
13440  {
13441  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
13442  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
13443  TrainDataVector.clear();
13444  Utilities->CallLogPop(1070);
13445  return false;
13446  }
13447  }
13448 
13449  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
13450  // i.e. a non repeating link to or from the shuttle service
13451  {
13452  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13453  {
13454  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
13455  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
13456  TrainDataVector.clear();
13457  Utilities->CallLogPop(1071);
13458  return false;
13459  }
13460  }
13461 /* it's allowed to have a different description
13462  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
13463  {
13464  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
13465  {
13466  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
13467  TrainDataVector.clear();
13468  Utilities->CallLogPop(1072);
13469  return false;
13470  }
13471  }
13472 */
13473  if(ForwardEntryPtr->Command == "Sns-sh")
13474  {
13475  if(ReverseEntryPtr->Command != "F-nshs")
13476  {
13477  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
13478  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
13479  TrainDataVector.clear();
13480  Utilities->CallLogPop(1073);
13481  return false;
13482  }
13483  }
13484 
13485  if(ForwardEntryPtr->Command == "F-nshs")
13486  {
13487  if(ReverseEntryPtr->Command != "Sns-sh")
13488  {
13489  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
13490  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
13491  TrainDataVector.clear();
13492  Utilities->CallLogPop(1074);
13493  return false;
13494  }
13495  else
13496  {
13497  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13498  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
13499  if(OtherTrainDataPtr->Description == "")
13500  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13501  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13502  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13503  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13504  }
13505  }
13506 
13507  if(ForwardEntryPtr->Command == "Sns-fsh")
13508  {
13509  if(ReverseEntryPtr->Command != "Fns-sh")
13510  {
13511  SecondPassMessage(GiveMessages,
13512  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
13513  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
13514  TrainDataVector.clear();
13515  Utilities->CallLogPop(1075);
13516  return false;
13517  }
13518  }
13519 
13520  if(ForwardEntryPtr->Command == "Fns-sh")
13521  {
13522  if(ReverseEntryPtr->Command != "Sns-fsh")
13523  {
13524  SecondPassMessage(GiveMessages,
13525  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
13526  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
13527  TrainDataVector.clear();
13528  Utilities->CallLogPop(1076);
13529  return false;
13530  }
13531  else
13532  {
13533  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
13534  // links to the non-repeating non-shuttle linked service
13535  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13536  // needed for creating formatted timetable
13537  if(OtherTrainDataPtr->Description == "")
13538  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13539  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13540  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13541  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13542  }
13543  }
13544 
13545  Utilities->CallLogPop(1077);
13546  return true;
13547 }
13548 
13549 // ---------------------------------------------------------------------------
13550 
13551 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
13552  // Forward train is the finish shuttle entry 'Fns-sh'.
13553  // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
13554 {
13555  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
13556  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
13557  int ForwardSecs = int(double(ForwardEventTime) * 86400);
13558  int ReverseSecs = int(double(ReverseEventTime) * 86400);
13559  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
13560 
13561  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
13562  {
13563  Utilities->CallLogPop(1369);
13564  return false;
13565  }
13566  else
13567  {
13568  Utilities->CallLogPop(1370);
13569  return true;
13570  }
13571 }
13572 
13573 // ---------------------------------------------------------------------------
13574 
13575 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
13576  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13577  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13578  // don't ever need to and as designed would skip repeats.
13579 
13580  // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
13581 {
13582  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
13583  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
13584  {
13585  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
13586  }
13587  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
13588  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
13589  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
13590 
13591  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
13592  {
13593  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
13594  TrainDataVector.clear();
13595  Utilities->CallLogPop(1091);
13596  return false;
13597  }
13598  while(LastActionCommand == "Fns")
13599  {
13600  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
13601  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
13602  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
13603  {
13604  SecondPassMessage(GiveMessages,
13605  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
13606  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
13607  TrainDataVector.clear();
13608  Utilities->CallLogPop(1092);
13609  return false;
13610  }
13611  }
13612  // exit the 'while' with LastActionCommand FSH-XX
13613  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
13614  {
13615  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
13616  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
13617  ". The linking of two or more shuttles is not permitted.");
13618  TrainDataVector.clear();
13619  Utilities->CallLogPop(1093);
13620  return false;
13621  }
13622  Utilities->CallLogPop(1094);
13623  return true;
13624 }
13625 
13626 // ---------------------------------------------------------------------------
13627 
13628 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
13629 {
13630  if(!GiveMessages)
13631  return;
13632 
13633  // if(ServiceReference == "") ShowMessage(Message);
13634  if(!CheckHeadCodeValidity(12, false, ServiceReference))
13635  ShowMessage(Message);
13636  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
13637  // false means don't give messages within the function
13638  else
13639  ShowMessage("Service " + ServiceReference + ": " + Message);
13640 }
13641 
13642 // ---------------------------------------------------------------------------
13643 
13644 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
13645 {
13646  if(!GiveMessages)
13647  return;
13648 
13649  ShowMessage(Message);
13650 }
13651 
13652 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
13653 // ---------------------------------------------------------------------------
13654 
13655 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
13656  // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
13657  // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
13658  // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
13659  // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
13660  // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
13661  // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
13662  // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
13663  // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
13664  // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
13665  // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
13666  // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
13667  // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
13668  // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
13669  // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
13670  // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
13671  // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
13672  // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
13673  // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
13674  // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
13675  // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
13676  // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
13677  // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
13678  // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
13679  // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
13680  // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
13681  // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
13682  // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
13683  // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
13684  // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
13685 {
13686  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
13687  AnsiString(ActionEventType) + "," + LocationID);
13688  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
13689 
13690  Prefix = " ERROR: ";
13691  if(ActionEventType == FailTrainEntry)
13692  {
13693  Prefix = " HELD: ";
13694  ErrorLog = " can't enter railway, train obstructing entry position ";
13695  WarningStr = " can't enter railway, train obstructing entry position ";
13696  Display->WarningLog(1, HeadCode + WarningStr + LocationID);
13697  }
13698  else if(ActionEventType == FailCreateTrain)
13699  {
13700  Prefix = " HELD: ";
13701  ErrorLog = " can't be created, train obstructing ";
13702  WarningStr = " can't be created, train obstructing ";
13703  Display->WarningLog(2, HeadCode + WarningStr + LocationID);
13704  }
13705  else if(ActionEventType == FailCreateLockedRoute)
13706  {
13707  Prefix = " HELD: ";
13708  ErrorLog = " can't be created on a locked route at ";
13709  WarningStr = " can't be created on a locked route at ";
13710  Display->WarningLog(4, HeadCode + WarningStr + LocationID);
13711  }
13712  else if(ActionEventType == FailEnterLockedRoute)
13713  {
13714  Prefix = " HELD: ";
13715  ErrorLog = " can't enter on a locked route at ";
13716  WarningStr = " can't enter on a locked route at ";
13717  Display->WarningLog(5, HeadCode + WarningStr + LocationID);
13718  }
13719  else if(ActionEventType == FailCreatePoints)
13720  {
13721  Prefix = " HELD: ";
13722  ErrorLog = " can't be created, diverging points at ";
13723  WarningStr = " can't be created, diverging points at ";
13724  Display->WarningLog(3, HeadCode + WarningStr + LocationID);
13725  }
13726  else if(ActionEventType == FailUnexpectedExitRailway)
13727  {
13728  ErrorLog = " left railway unexpectedly at ";
13729  UnexpectedExits++;
13730  }
13731  else if(ActionEventType == FailIncorrectExit)
13732  {
13733  ErrorLog = " left railway at an incorrect exit at ";
13734  IncorrectExits++;
13735  }
13736  else if(ActionEventType == FailLocTooShort)
13737  {
13738  ErrorLog = " failed to split - location too short at ";
13739  WarningStr = " failed to split, location too short at ";
13740  Display->WarningLog(6, HeadCode + WarningStr + LocationID);
13741  }
13742  else if(ActionEventType == FailSplitDueToOtherTrain)
13743  {
13744  Prefix = " HELD: ";
13745  ErrorLog = " unable to split - other train obstructing at ";
13746  WarningStr = " unable to split - other train obstructing at ";
13747  Display->WarningLog(7, HeadCode + WarningStr + LocationID);
13748  }
13749  else if(ActionEventType == FailUnexpectedBuffers)
13750  ErrorLog = " stopped at buffers unexpectedly at position ";
13751  else if(ActionEventType == FailMissedArrival)
13752  {
13753  ErrorLog = " failed to stop at ";
13754  MissedStops++;
13755  }
13756  else if(ActionEventType == FailMissedSplit)
13757  {
13758  ErrorLog = " failed to split at ";
13760  }
13761  else if(ActionEventType == FailMissedJBO)
13762  {
13763  ErrorLog = " failed to be joined by other train at ";
13765  }
13766  else if(ActionEventType == FailMissedJoinOther)
13767  {
13768  ErrorLog = " failed to join other train at ";
13770  }
13771  else if(ActionEventType == FailMissedTerminate)
13772  {
13773  ErrorLog = " failed to terminate at ";
13775  }
13776  else if(ActionEventType == FailMissedNewService)
13777  {
13778  ErrorLog = " failed to form new service at ";
13780  }
13781  else if(ActionEventType == FailMissedExitRailway)
13782  {
13783  ErrorLog = " failed to exit railway ";
13785  }
13786  else if(ActionEventType == FailMissedChangeDirection)
13787  {
13788  ErrorLog = " failed to change direction at ";
13790  }
13791  else if(ActionEventType == FailMissedPass)
13792  {
13793  ErrorLog = " failed to pass ";
13795  }
13796  else if(ActionEventType == FailBuffersPreventingStart)
13797  ErrorLog = " facing buffers and unable to start at ";
13798  else if(ActionEventType == FailDerailed)
13799  {
13800  ErrorLog = " DERAILED at position ";
13801  Prefix = " DERAILMENT: ";
13802  Derailments++;
13803  }
13804  else if(ActionEventType == FailBufferCrash)
13805  {
13806  ErrorLog = " CRASHED INTO BUFFERS at ";
13807  Prefix = " CRASH: ";
13808  CrashedTrains++;
13809  }
13810  else if(ActionEventType == FailLevelCrossingCrash)
13811  {
13812  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
13813  Prefix = " CRASH: ";
13814  CrashedTrains++;
13815  }
13816  else if(ActionEventType == FailCrashed)
13817  {
13818  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
13819  Prefix = " CRASH: ";
13820  CrashedTrains++;
13821  CrashedTrains++;
13822  }
13823  else if(ActionEventType == FailSPAD)
13824  {
13825  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
13826  Prefix = " SPAD: ";
13827  SPADEvents++;
13828  }
13829  else if(ActionEventType == FailLockedRoute)
13830  {
13831  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
13832  Prefix = " SPAD RISK: ";
13833  SPADRisks++;
13834  }
13835  else if(ActionEventType == RouteForceCancelled)
13836  {
13837  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
13838  }
13839  else if(ActionEventType == WaitingForJBO)
13840  {
13841  Prefix = " WARNING: ";
13842  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
13843  WarningStr = " waiting to join " + OtherHeadCode + " at ";
13844  Display->WarningLog(8, HeadCode + WarningStr + LocationID);
13845  }
13846  else if(ActionEventType == WaitingForFJO)
13847  {
13848  Prefix = " WARNING: ";
13849  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
13850  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
13851  Display->WarningLog(9, HeadCode + WarningStr + LocationID);
13852  }
13853  TDateTime ActualTime = TrainController->TTClockTime;
13854 
13855  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
13856  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
13857  Utilities->CallLogPop(1371);
13858 }
13859 
13860 // ---------------------------------------------------------------------------
13861 
13863 {
13864 /*
13865  TrainDataEntry
13866  AnsiString HeadCode, Description;//null on creation
13867  int StartSpeed, MaxRunningSpeed;//both kph
13868  int RepeatNumber;
13869  TActionVector ActionVector;
13870  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
13871  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
13872 
13873  ActionVectorEntry
13874  TTimetableEntryType FormatType;
13875  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
13876  AnsiString LocationName, Command, OtherHeadCode;//null on creation
13877  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
13878  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
13879  int RepeatNumber;
13880 
13881  TrainOperatingData
13882  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
13883  int TrainID;
13884  TRunningEntry RunningEntry;
13885  TDateTime StartTime;
13886  AnsiString HeadCode;
13887 */
13888  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
13889  std::ofstream OutFile("TrainData.csv");
13890 
13891  if(OutFile == 0)
13892  {
13893  ShowMessage("Output file TrainData.csv failed to open");
13894  Utilities->CallLogPop(1372);
13895  return;
13896  }
13897  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13898  {
13899  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13900  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
13901 
13902  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
13903  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
13904 
13905  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
13906  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
13907  "RepeatNumber" << '\n' << '\n';
13908  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13909  {
13910  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13911  AnsiString TimetableEntryTypeStr;
13912  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
13913  switch(AVEntry.FormatType)
13914  {
13915  case 0:
13916  {
13917  TimetableEntryTypeStr = "NoFormat";
13918  break;
13919  }
13920 
13921  case 1:
13922  {
13923  TimetableEntryTypeStr = "TimeLoc";
13924  break;
13925  }
13926 
13927  case 2:
13928  {
13929  TimetableEntryTypeStr = "TimeTimeLoc";
13930  break;
13931  }
13932 
13933  case 3:
13934  {
13935  TimetableEntryTypeStr = "TimeCmd";
13936  break;
13937  }
13938 
13939  case 4:
13940  {
13941  TimetableEntryTypeStr = "StartNew";
13942  break;
13943  }
13944 
13945  case 5:
13946  {
13947  TimetableEntryTypeStr = "TimeCmdHeadCode";
13948  break;
13949  }
13950 
13951  case 6:
13952  {
13953  TimetableEntryTypeStr = "FinRemHere";
13954  break;
13955  }
13956 
13957  case 7:
13958  {
13959  TimetableEntryTypeStr = "FNSShuttle";
13960  break;
13961  }
13962 
13963  case 8:
13964  {
13965  TimetableEntryTypeStr = "SNTShuttle";
13966  break;
13967  }
13968 
13969  case 9:
13970  {
13971  TimetableEntryTypeStr = "SNSShuttle";
13972  break;
13973  }
13974 
13975  case 10:
13976  {
13977  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
13978  break;
13979  }
13980 
13981  case 11:
13982  {
13983  TimetableEntryTypeStr = "FSHNewService";
13984  break;
13985  }
13986 
13987  case 12:
13988  {
13989  TimetableEntryTypeStr = "Repeat";
13990  break;
13991  }
13992 
13993  default:
13994  {
13995  TimetableEntryTypeStr = "Default";
13996  break;
13997  }}
13998  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
13999  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
14000  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
14001  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
14002  AVEntry.NumberOfRepeats << '\n';
14003  }
14004  OutFile << '\n';
14005  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
14006  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
14007  {
14008  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
14009  AnsiString RunningEntryStr;
14010  // NotStarted, Running, Exited
14011  switch(TOD.RunningEntry)
14012  {
14013  case 0:
14014  {
14015  RunningEntryStr = "NotStarted";
14016  break;
14017  }
14018 
14019  case 1:
14020  {
14021  RunningEntryStr = "Running";
14022  break;
14023  }
14024 
14025  case 2:
14026  {
14027  RunningEntryStr = "Exited";
14028  break;
14029  }}
14030  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
14031  }
14032  OutFile << '\n';
14033  }
14034  OutFile.close();
14035  Utilities->CallLogPop(1373);
14036 }
14037 
14038 // ---------------------------------------------------------------------------
14039 
14040 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
14041  // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
14042  // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
14043 {
14044  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
14045  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
14047  ShowMessage(Message);
14048  BaseTime = TDateTime::CurrentDateTime();
14049  StopTTClockFlag = false;
14050  Utilities->CallLogPop(1374);
14051 }
14052 
14053 // ---------------------------------------------------------------------------
14054 
14055 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
14056  // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
14057  // from the start of the relevant vectors. Can't save the pointer values
14058  // as these will be different each time the vectors are created
14059 {
14060  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
14061  Utilities->SaveFileInt(SessionFile, TrainVector.size());
14062  for(unsigned int x = 0; x < TrainVector.size(); x++)
14063  {
14064  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
14065  }
14066  Utilities->CallLogPop(1375);
14067 }
14068 
14069 // ---------------------------------------------------------------------------
14070 
14071 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
14072 {
14073  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
14074  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
14075  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14076 
14077  // by zero error in calculating AValue, use 1
14078  for(int x = 0; x < NumberOfTrains; x++)
14079  {
14080  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
14081  // by zero error in calculating AValue, use 1
14082  NewTrain->LoadOneSessionTrain(0, SessionFile);
14083  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
14084  // added at v2.4.0. have to include as that value not stored in session file
14085  {
14086  NewTrain->StoppedWithoutPower = true;
14087  }
14088  TrainVector.push_back(*NewTrain);
14089  LastTrainLoaded = x;
14090  }
14091  delete NewTrain;
14092  Utilities->CallLogPop(1376);
14093 }
14094 
14095 // ---------------------------------------------------------------------------
14096 
14097 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
14098 {
14099  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
14100  int NumberOfTrains;
14101 
14102  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
14103  {
14104  Utilities->CallLogPop(1377);
14105  return false;
14106  }
14107  for(int x = 0; x < NumberOfTrains; x++)
14108  {
14109  if(!(TTrain::CheckOneSessionTrain(InFile)))
14110  {
14111  Utilities->CallLogPop(1378);
14112  return false;
14113  }
14114  }
14115  Utilities->CallLogPop(1379);
14116  return true;
14117 }
14118 
14119 // ---------------------------------------------------------------------------
14120 
14121 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
14122 {
14123  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
14124  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
14125  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
14126  {
14127  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
14128  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
14129  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
14130  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
14131  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
14132  }
14133  Utilities->CallLogPop(1380);
14134 }
14135 
14136 // ---------------------------------------------------------------------------
14137 
14138 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14139 {
14140  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
14141  TAllRoutes::TLockedRouteClass LockedRouteObject;
14142  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
14143 
14144  for(int x = 0; x < LockedRouteVectorSize; x++)
14145  {
14146  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14147  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14148  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
14149  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
14150  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
14151  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
14152  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
14153  }
14154  Utilities->CallLogPop(1381);
14155 }
14156 
14157 // ---------------------------------------------------------------------------
14158 
14159 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
14160 {
14161  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
14162  int LockedRouteVectorSize;
14163 
14164  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
14165  {
14166  Utilities->CallLogPop(1382);
14167  return false;
14168  }
14169  for(int x = 0; x < LockedRouteVectorSize; x++)
14170  {
14171  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14172  {
14173  Utilities->CallLogPop(1383);
14174  return false;
14175  }
14176  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14177  {
14178  Utilities->CallLogPop(1384);
14179  return false;
14180  }
14181  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14182  {
14183  Utilities->CallLogPop(1385);
14184  return false;
14185  }
14186  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14187  {
14188  Utilities->CallLogPop(1386);
14189  return false;
14190  }
14191  if(!Utilities->CheckFileDouble(SessionFile))
14192  {
14193  Utilities->CallLogPop(1387);
14194  return false;
14195  }
14196  }
14197  Utilities->CallLogPop(1388);
14198  return true;
14199 }
14200 
14201 // ---------------------------------------------------------------------------
14202 
14203 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
14204 {
14205  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
14206  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
14207  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
14208  {
14209  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
14210  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
14211  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
14212  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
14213  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
14214  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
14215  }
14216  Utilities->CallLogPop(1389);
14217 }
14218 
14219 // ---------------------------------------------------------------------------
14220 
14221 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
14222 {
14223  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
14224  TContinuationAutoSigEntry ContinuationAutoSigObject;
14225  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
14226 
14227  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
14228  {
14229  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
14230  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
14231  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
14232  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
14233  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
14234  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
14235  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
14236  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
14237  }
14238  Utilities->CallLogPop(1390);
14239 }
14240 
14241 // ---------------------------------------------------------------------------
14242 
14243 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
14244 {
14245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
14246  int ContinuationAutoSigVectorSize;
14247 
14248  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
14249  {
14250  Utilities->CallLogPop(1391);
14251  return false;
14252  }
14253  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
14254  {
14255  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
14256  {
14257  Utilities->CallLogPop(1392);
14258  return false;
14259  }
14260  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
14261  {
14262  Utilities->CallLogPop(1393);
14263  return false;
14264  }
14265  if(!Utilities->CheckFileDouble(SessionFile))
14266  {
14267  Utilities->CallLogPop(1405);
14268  return false;
14269  }
14270  if(!Utilities->CheckFileDouble(SessionFile))
14271  {
14272  Utilities->CallLogPop(1406);
14273  return false;
14274  }
14275  if(!Utilities->CheckFileDouble(SessionFile))
14276  {
14277  Utilities->CallLogPop(1407);
14278  return false;
14279  }
14280  if(!Utilities->CheckFileDouble(SessionFile))
14281  {
14282  Utilities->CallLogPop(1394);
14283  return false;
14284  }
14285  }
14286  Utilities->CallLogPop(1395);
14287  return true;
14288 }
14289 
14290 // ---------------------------------------------------------------------------
14291 
14292 /*
14293  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
14294  {
14295  public:
14296  AnsiString Description; ///< service description
14297  AnsiString HeadCode; ///< service headcode
14298  int RepeatNumber; ///< service RepeatNumber
14299  int IncrementalMinutes; ///< Repeat separation in minutes
14300  int IncrementalDigits; ///< Repeat headcode separation
14301  int VectorPosition; ///< TrackVectorPosition for the continuation element
14302  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
14303  };
14304 
14305 
14306  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
14307  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
14308 */
14309 
14311  // build this into timetable load so session loading can use it too
14312  // being a multimap it automatically sorts in ascending EventTime order
14313 {
14314  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
14316  // need to clear as this called twice when load a session
14317  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14318  {
14319  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14320  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
14321  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14322 
14323  if(AVFirstEntry.Command == "Snt")
14324  // new train (no need to include Snt-sh since they can't start at a continuation)
14325  {
14328  {
14330  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
14331  // retains this value for all repeats
14332  CTEEntry.RepeatNumber = 0; // for first entry
14333  CTEEntry.TrainDataEntryPtr = &TDEntry;
14334  // retains this value for all repeats
14335  CTEEntry.HeadCode = TDEntry.HeadCode;
14336  CTEEntry.Description = TDEntry.Description;
14337  CTEEntry.IncrementalMinutes = 0;
14338  CTEEntry.IncrementalDigits = 0;
14339  if(AVLastEntry.FormatType == Repeat)
14340  {
14341  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
14342  // retains this value or 0 for all repeats
14343  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
14344  // retains this value or 0 for all repeats
14345  }
14346  CTEMMP.first = AVFirstEntry.EventTime;
14347  CTEMMP.second = CTEEntry;
14348  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
14349  // base entry
14350  if(TDEntry.NumberOfTrains > 1)
14351  {
14352  if(AVLastEntry.FormatType != Repeat)
14353  {
14354  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
14355  }
14356  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14357  {
14358  CTEEntry.RepeatNumber = y;
14359  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
14360  // CTEEntry.VectorPosition stays same
14361  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
14362  CTEMMP.second = CTEEntry;
14363  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
14364  }
14365  }
14366  }
14367  }
14368  }
14369  Utilities->CallLogPop(1396);
14370 }
14371 
14372 // ---------------------------------------------------------------------------
14373 
14375 {
14376  // called when WarningFlashCount == 0 or when press zoomout button
14377  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
14378  if(!Display->ZoomOutFlag)
14379  {
14380  Utilities->CallLogPop(1156);
14381  return;
14382  }
14383  for(unsigned int x = 0; x < TrainVector.size(); x++)
14384  {
14385  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
14386  // if OldPlotElement[x] == -1 then ignore (not plotted)
14388  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
14389  }
14390  Display->Update();
14391  // need to keep this since Update() not called for PlotSmallOutput as too slow
14392  Utilities->CallLogPop(742);
14393 }
14394 
14395 // ---------------------------------------------------------------------------
14396 
14397 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
14398 {
14399  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
14400  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
14401  {
14402  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
14403  }
14404  Utilities->CallLogPop(740);
14405  return TrainVector.at(VecPos);
14406 }
14407 
14408 // ---------------------------------------------------------------------------
14409 
14410 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
14411 {
14412  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
14413  AnsiString RetStr = "", PartStr = "";
14414 
14415 
14416 /*
14417  Have description & mass etc for train at top - header, then array of actions
14418 
14419  class TActionVectorEntry
14420  {
14421  public:
14422  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
14424  bool SignallerControl;
14426  bool Warning;
14428  int NumberOfRepeats;
14430  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
14432  TDateTime EventTime, ArrivalTime, DepartureTime;
14434  TExitList ExitList;
14436  TTimetableFormatType FormatType;
14438  TTimetableLocationType LocationType;
14440  TTimetableSequenceType SequenceType;
14442  TTimetableShuttleLinkType ShuttleLinkType;
14444  TTrainDataEntry *LinkedTrainEntryPtr;
14446  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
14448 
14449  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
14450 
14451  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
14452 
14453  class TTrainOperatingData
14454  {
14455  public:
14456  int TrainID;
14457  TActionEventType EventReported;
14458  TRunningEntry RunningEntry;
14459 
14460  //inline function
14461  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
14462  };
14463 
14464  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
14465 
14466  class TTrainDataEntry
14467  {
14468  public:
14469  AnsiString HeadCode, ServiceReference, Description;
14471  double MaxBrakeRate;
14473  double MaxRunningSpeed;
14475  double PowerAtRail;
14477  int Mass;
14479  int NumberOfTrains;
14481  int SignallerSpeed;
14483  int StartSpeed;
14485  TActionVector ActionVector;
14487  TTrainOperatingDataVector TrainOperatingDataVector;
14489 
14490  //inline function
14491  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
14492  };
14493 
14494  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
14495 
14496  //formatted timetable types
14497  class TOneTrainFormattedEntry
14498  {
14499  AnsiString Action;//includes location if relevanr
14500  AnsiString Time;
14501  };
14502 
14503  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
14504 
14505  class TOneCompleteFormattedTrain//headcode + list of actions
14506  {
14507  public:
14508  AnsiString HeadCode;
14509  TOneFormattedTrainVector OneFormattedTrainVector;
14510  };
14511 
14512  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
14513 
14514  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
14515  {
14516  public:
14517  AnsiString Header;//description, mass, power, brake rate etc
14518  int NumberOfTrains;// number of repeats + 1
14519  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
14520  };
14521 
14522 
14523  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
14524  //end of formatted timetable types
14525 
14526 */
14527 
14528  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
14529 
14530  // format "16/06/2009 20:55:17"
14531  // avoid characters in filename:= / \ : * ? " < > |
14532  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
14533 
14534  AnsiString ShortTTName = "";
14535 
14536  for(int x = TTFileName.Length(); x > 0; x--)
14537  {
14538  if(TTFileName[x] == '\\')
14539  {
14540  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
14541  break;
14542  }
14543  }
14544 
14545  ShowMessage("Creates two timetables named " + ShortTTName +
14546  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
14547 
14548  Screen->Cursor = TCursor(-11); // Hourglass
14549 
14550  AnsiString FormatNoDPStr = "#######0";
14551  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
14552 
14554  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
14555  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
14556 
14557  // all timetable in formatted form
14558  //create the AllTTTrains vector
14559  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14560  {
14561  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
14562  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
14563  if(TrainDataEntry.Mass > 0)
14564  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
14565  if(TrainDataEntry.PowerAtRail > 0)
14566  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
14567  if(TrainDataEntry.MaxBrakeRate > 0)
14568  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
14569  if(TrainDataEntry.MaxRunningSpeed > 0)
14570  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
14571  FirstHeadCode = TrainDataEntry.HeadCode;
14572  int IncDigits = 0, IncMinutes = 0;
14573  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
14574  if(!ActionVector.empty())
14575  {
14576  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
14577  {
14578  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
14579  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
14580  }
14581  }
14582 
14583  TTrainFormattedInformation OneTTLine;
14584  // contains all information for a single TT entry (including repeats)
14585  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
14586  {
14587  OneTTLine.Header = "";
14588  if((TrainDataEntry.Description != "") && (MassStr != ""))
14589  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
14590  else if(TrainDataEntry.Description != "")
14591  OneTTLine.Header = TrainDataEntry.Description;
14592  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
14593  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
14594  for(unsigned int z = 0; z < ActionVector.size(); z++)
14595  {
14596  TOneTrainFormattedEntry OneTTEntry;
14597  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
14598  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
14599  AnsiString PartStr = "", TimeStr = "";
14600 /*
14601  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
14602  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
14603  ExitRailway};
14604  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
14605  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
14606  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
14607 */
14608  if(ActionVectorEntry.SequenceType == Start)
14609  {
14610  if(ActionVectorEntry.FormatType == StartNew)
14611  {
14612  if(ActionVectorEntry.LocationName != "")
14613  {
14614  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
14615  {
14616  PartStr = "Enters at " + ActionVectorEntry.LocationName;
14617  }
14618  else
14619  {
14620  PartStr = "Created at " + ActionVectorEntry.LocationName;
14621  }
14622  }
14623  else // may be a named continuation or other element, and if so report that
14624  {
14625  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
14626  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
14627  {
14628  if(LocName != "")
14629  {
14630  PartStr = "Enters at " + LocName;
14631  }
14632  else // use rear position if it's a continuation
14633  {
14634  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
14635  }
14636  }
14637  else // not a continuation
14638  {
14639  if(LocName != "")
14640  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
14641  // but include anyway
14642  {
14643  PartStr = "Created at " + LocName;
14644  }
14645  else // use rear position again
14646  {
14647  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
14648  }
14649  }
14650  }
14651  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
14652  }
14653  else if(ActionVectorEntry.FormatType == SNTShuttle)
14654  {
14655  if(y == 0) // first train
14656  {
14657  PartStr = "Enters at " + ActionVectorEntry.LocationName;
14658  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
14659  }
14660  else
14661  {
14662  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
14663  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
14664  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
14665  } // y-1 for headcode above since it is the last repeat value that the train is from
14666  }
14667  else if(ActionVectorEntry.Command == "Sfs")
14668  {
14669  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
14670  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14671  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
14672  }
14673  else if(ActionVectorEntry.Command == "Sns")
14674  {
14675  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14676  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14677  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
14678  }
14679  else if(ActionVectorEntry.FormatType == SNSShuttle)
14680  {
14681  if(y == 0) // first entry from shuttle
14682  {
14683  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14684  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
14685  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
14686  }
14687  else
14688  {
14689  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
14690  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
14691  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
14692  } // y-1 for headcode above since it is the last repeat value that the train is from
14693  }
14694  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
14695  {
14696  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
14697  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
14698  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
14699  AnsiString FirstHeadCode = TDE->HeadCode;
14700  int LastRepeatNumber = TDE->NumberOfTrains - 1;
14701  // a shuttle has to have at least 1 repeat
14702  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
14703  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
14704  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
14705  }
14706  }
14707  else if(ActionVectorEntry.SequenceType == Intermediate)
14708  {
14709  if(ActionVectorEntry.FormatType == TimeTimeLoc)
14710  {
14711  // here need 2 entries if times different so push the first right away & the second later
14712  // if times same just give the arrival entry
14713  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
14714  {
14715  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
14716  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14717  OneTTEntry.Action = PartStr;
14718  OneTTEntry.Time = TimeStr;
14719  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
14720  PartStr = "Departs from " + ActionVectorEntry.LocationName;
14721  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
14722  }
14723  else
14724  {
14725  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
14726  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14727  }
14728  }
14729  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
14730  {
14731  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
14732  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
14733  }
14734  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
14735  {
14736  PartStr = "Departs from " + ActionVectorEntry.LocationName;
14737  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
14738  }
14739  else if(ActionVectorEntry.FormatType == PassTime)
14740  {
14741  PartStr = "Passes " + ActionVectorEntry.LocationName;
14742  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
14743  }
14744  else if(ActionVectorEntry.Command == "jbo")
14745  {
14746  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
14747  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14748  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
14749  }
14750  else if(ActionVectorEntry.Command == "fsp")
14751  {
14752  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
14753  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14754  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
14755  }
14756  else if(ActionVectorEntry.Command == "rsp")
14757  {
14758  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
14759  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14760  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
14761  }
14762  else if(ActionVectorEntry.Command == "cdt")
14763  {
14764  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
14765  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
14766  }
14767  }
14768  else if(ActionVectorEntry.SequenceType == Finish)
14769  {
14770  if(ActionVectorEntry.Command == "Fns")
14771  {
14772  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14773  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14774  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
14775  }
14776  else if(ActionVectorEntry.Command == "F-nshs")
14777  {
14778  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14779  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
14780  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
14781  }
14782  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
14783  {
14784  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
14785  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
14786  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
14787  // y+1 because it's the NEXT service repeat number that is relevant
14788  }
14789  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
14790  {
14791  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14792  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
14793  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
14794  }
14795  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
14796  {
14797  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
14798  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
14799  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
14800  // y+1 because it's the NEXT service repeat number that is relevant
14801  }
14802  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
14803  {
14804  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
14805  // only used in chronological tt
14806  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
14807  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
14808  }
14809  else if(ActionVectorEntry.Command == "Frh")
14810  {
14811  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
14812  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
14813  if(z > 0)
14814  // should be for finish entry but include check for safety
14815  {
14816  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
14817  {
14818  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
14819  }
14820  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
14821  {
14822  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
14823  }
14824  else
14825  TimeStr = " "; // shouldn't ever get here
14826  }
14827  }
14828  else if(ActionVectorEntry.Command == "Fer")
14829  {
14830  AnsiString AllowedExits;
14831  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
14832  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
14833  }
14834  else if(ActionVectorEntry.Command == "Fjo")
14835  {
14836  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
14837  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
14838  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
14839  }
14840  }
14841  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
14842  continue; // no entry needed for a repeat
14843  OneTTEntry.Action = PartStr;
14844  OneTTEntry.Time = TimeStr;
14845  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
14846  // one per action
14847  }
14848  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
14849  // one per repeat
14850  }
14851  AllTTTrains->push_back(OneTTLine); // one per repeating train
14852  }
14853  // AllTTTrains vector now complete
14854 
14855  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
14856 
14857  if(TTFile == 0)
14858  {
14859  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
14860  delete AllTTTrains;
14861  Utilities->CallLogPop(1567);
14862  return;
14863  }
14864 
14865 /* formatted timetable types
14866  class TOneTrainFormattedEntry
14867  {
14868  AnsiString Action;//includes location if relevant
14869  AnsiString Time;
14870  };
14871 
14872  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
14873 
14874  class TOneCompleteFormattedTrain//headcode + list of actions
14875  {
14876  public:
14877  AnsiString HeadCode;
14878  TOneFormattedTrainVector OneFormattedTrainVector;
14879  };
14880 
14881  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
14882 
14883  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
14884  {
14885  public:
14886  AnsiString Header;//description, mass, power, brake rate etc
14887  int NumberOfTrains;// number of repeats + 1
14888  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
14889  };
14890 
14891  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
14892  //end of formatted timetable types
14893 */
14894 
14895  // new layout using multiple rows
14896  TTFile << TableTitle.c_str() << '\n' << '\n';
14897  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
14898  {
14899  TTFile << AllTTTrains->at(x).Header.c_str();
14900  TTFile << '\n';
14901  TTFile << ','; // for the blank line above the action list
14902  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14903  {
14904  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
14905  {
14906  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
14907  }
14908  else
14909  {
14910  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
14911  }
14912  }
14913  TTFile << '\n' << '\n';
14914 
14915  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
14916  {
14917  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
14918  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14919  {
14920  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
14921  {
14922  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
14923  }
14924  else
14925  {
14926  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
14927  }
14928  }
14929  TTFile << '\n';
14930  }
14931  TTFile << '\n' << '\n';
14932  }
14933 
14934  TTFile.close();
14935 
14936  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
14937 
14938  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
14939 
14940  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
14941 
14942  if(TTFile2 == 0)
14943  {
14944  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
14945  delete AllTTTrains;
14946  Utilities->CallLogPop(1710);
14947  return;
14948  }
14949 
14950  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
14951  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
14952  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
14953 
14954  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
14955  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
14956 
14957  // multimap of AnsiStrings with TimeString as key (to sort automatically)
14958 
14959  TTFile2 << TableTitle.c_str() << '\n' << '\n';
14960  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
14961  {
14962  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
14963  {
14964  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
14965  {
14966  bool GiveMessagesFalse = false;
14967  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
14968  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
14969  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
14970  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
14971  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
14972  { // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
14973  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
14974  TimeString = TimeString.SubString(9, 5);
14975  ActionString += " " + OtherHeadCode;
14976  }
14977  if(TimeString.SubString(1, 7) == "End at ")
14978  // for Frh-sh final entry
14979  {
14980  TimeString = TimeString.SubString(8, 5);
14981  }
14982  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
14983  AnsiMultiMapEntry.first = TimeString;
14984  AnsiMultiMapEntry.second = OneLine;
14985  TAMM->insert(AnsiMultiMapEntry);
14986  }
14987  }
14988  }
14989 
14990  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
14991  {
14992  TTFile2 << (AMMIT->second).c_str();
14993  }
14994  delete AllTTTrains;
14995  delete TAMM;
14996  TTFile2.close();
14997  Utilities->CallLogPop(1580);
14998 }
14999 
15000 // ---------------------------------------------------------------------------
15001 
15002 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
15003  bool AtLocChecked, int ArrRange, int DepRange)
15004 {
15005 
15006  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
15007  bool AnalysisError = false;
15008 
15009  try
15010  {
15011  //New section after v2.4.3 for tt conflict analysis
15012  /*
15013  typedef std::list<AnsiString> TServiceCallingLocsList;
15014  typedef std::vector<TServiceCallingLocsList> TAllServiceCallingLocsMap;
15015 
15016  struct TLocServiceTimes
15017  {
15018  AnsiString Location;
15019  AnsiString ServiceAndRepeatNum;
15020  AnsiString AtLocTime;
15021  AnsiString ArrTime;
15022  AnsiString DepTime;
15023  };
15024  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15025  */
15026 
15027  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
15028  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
15029 
15030  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
15031  TTrainDataVector::iterator TDVIt, TDVCopyIt;
15032  int Suffix = 0;
15033  int IteratorNumber = 0;
15034  AnsiString AnsiSuffix = "";
15035  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
15036  {
15037  IteratorNumber++; //first value in loop is 1
15038  Suffix = 0;
15039  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
15040  {
15041  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
15042  {
15043  Suffix++; //first value is 1
15044  AnsiSuffix = AnsiString(Suffix);
15045  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
15046  }
15047  }
15048  }
15049 
15050  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
15051  TServiceCallingLocsList ServiceCallingLocsList;
15052  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
15053  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15054  {
15055  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15056  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15057  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
15058  ServiceCallingLocsList.clear();
15059  if(ActionVector.empty())
15060  {
15061  continue;
15062  }
15063  if(ActionVector.at(0).SignallerControl)
15064  {
15065  continue;
15066  }
15067  for(unsigned int z = 0; z < ActionVector.size(); z++)
15068  {
15069  TActionVectorEntry AVE = ActionVector.at(z);
15070  if(AVE.FormatType == StartNew)
15071  {
15072  if(AVE.LocationType == AtLocation) //located Snt
15073  {
15074  ServiceCallingLocsList.push_back(AVE.LocationName);
15075  }
15076  else //unlocated Snt (could be entering at continuation)
15077  {
15079  if(TE.ActiveTrackElementName != "")
15080  {
15081  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
15082  }
15083  else
15084  {
15085  int HLoc = TE.HLoc;
15086  int VLoc = TE.VLoc;
15087  AnsiString HString;
15088  AnsiString VString;
15089  if(HLoc < 0)
15090  {
15091  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15092  }
15093  else
15094  {
15095  HString = AnsiString(HLoc);
15096  }
15097  if(VLoc < 0)
15098  {
15099  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15100  }
15101  else
15102  {
15103  VString = AnsiString(VLoc);
15104  }
15105  ServiceCallingLocsList.push_back(HString + '-' + VString);
15106  }
15107  }
15108  }
15109  else if(AVE.SequenceType == Start) //other start entries, all located
15110  {
15111  ServiceCallingLocsList.push_back(AVE.LocationName);
15112  }
15113  else if(AVE.FormatType == TimeLoc) //z must be > 0
15114  {
15115  if(ServiceCallingLocsList.back() != AVE.LocationName)
15116  {
15117  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
15118  }
15119  }
15120  else if(AVE.FormatType == PassTime)
15121  {
15122  ServiceCallingLocsList.push_back(AVE.LocationName);
15123  }
15124  else if(AVE.FormatType == TimeTimeLoc)
15125  {
15126  ServiceCallingLocsList.push_back(AVE.LocationName);
15127  }
15128  else if(AVE.Command == "cdt") //list if not next to start or finish
15129  {
15130  if(ActionVector.at(z-1).SequenceType == Start)
15131  {
15132  continue;
15133  }
15134  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
15135  {
15136  continue;
15137  }
15138  else
15139  {
15140  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
15141  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
15142  }
15143  }
15144  else if(AVE.FormatType == ExitRailway) //Fer
15145  {
15146  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
15147  AnsiString LName = TE.ActiveTrackElementName;
15148  if(LName != "")
15149  {
15150  ServiceCallingLocsList.push_back(LName);
15151  }
15152  else
15153  {
15154  int HLoc = TE.HLoc;
15155  int VLoc = TE.VLoc;
15156  AnsiString HString;
15157  AnsiString VString;
15158  if(HLoc < 0)
15159  {
15160  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15161  }
15162  else
15163  {
15164  HString = AnsiString(HLoc);
15165  }
15166  if(VLoc < 0)
15167  {
15168  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15169  }
15170  else
15171  {
15172  VString = AnsiString(VLoc);
15173  }
15174  ServiceCallingLocsList.push_back(HString + '-' + VString);
15175  }
15176  }
15177  }
15178  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
15179  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
15180  }
15181  //AllServiceCallingLocsMap built
15182 
15183  //test validity of AllServiceCallingLocsMap
15184 /*
15185  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
15186  std::ofstream Test(TestFile.c_str());
15187 
15188  if(TestFile == 0)
15189  {
15190  ShowMessage("TestFile failed to open - can't be created");
15191  Utilities->CallLogPop();
15192  return false;
15193  }
15194 
15195  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
15196  {
15197  Test << ASCLIt->first << '\n'; //service ref
15198  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
15199  {
15200  Test << *SCLIt << '\n';
15201  }
15202  Test << "\n\n";
15203  }
15204  Test.close();
15205  Utilities->CallLogPop();
15206  return true;
15207 */
15208 
15209  //initialise variables before calc LastTTTime & build LocServiceTimesVector
15210  if(TrainDataVector.empty())
15211  {
15212  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
15213  Utilities->CallLogPop(2209);
15214  return false;
15215  }
15216  TLocServiceTimes TLSTEntry;
15217  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
15218  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
15219  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
15220  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
15221  LastTTTime = "";
15222 
15223  //calculate LastTTTime
15224  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15225  {
15226  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15227  TActionVector &ActionVector = TrainDataEntry.ActionVector;
15228  TActionVectorIterator AVLast = ActionVector.end() - 1;//points to last entry
15229  TDateTime LastTDTime;
15230  int IncMinutes = 0;
15231  NumTrains = TrainDataEntry.NumberOfTrains;
15232  if(ActionVector.empty())
15233  {
15234  continue;
15235  }
15236  if(ActionVector.at(0).SignallerControl)
15237  {
15238  continue;
15239  }
15240  if(AVLast->FormatType == Repeat)
15241  {
15242  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15243  AVLast--; //now points to the command before the repeat
15244  }
15245  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
15246  {
15247  AVLast--; //points to last timed entry
15248  }
15249  //here AVLast points to last entry with a time
15250  if(AVLast->ArrivalTime != TDateTime(-1))
15251  {
15252  LastTDTime = AVLast->ArrivalTime;
15253  }
15254  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
15255  {
15256  LastTDTime = AVLast->EventTime;
15257  }
15258  else
15259  {
15260  continue; //shouldn't ever reach here but if do then skip this service
15261  }
15262  if(NumTrains == 1)
15263  {
15264  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
15265  }
15266  else
15267  {
15268  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
15269  }
15270  if(LastAnsiTime > LastTTTime)
15271  {
15272  LastTTTime = LastAnsiTime;
15273  }
15274  }
15275 
15276 //build LocServiceTimesVector
15277 
15278 /* struct TLocServiceTimes
15279  {
15280  AnsiString Location;
15281  AnsiString ServiceAndRepeatNum;
15282  AnsiString AtLocTime;
15283  AnsiString ArrTime;
15284  AnsiString DepTime;
15285  };
15286  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
15287 
15288 This works as follows:
15289 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
15290 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
15291 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
15292 
15293 Every action for every train is examined and times entered as follows:-
15294 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
15295 b) an unlocated Snt: entry time becomes DepTime
15296 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
15297 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
15298 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
15299 f) TimeTimeLoc: Arrival time entered as ErrTime, a check if Arr & Dep same and if s go in as one entry, else all minutes between entered as AtLocs then DepTime
15300 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
15301 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
15302 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
15303 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
15304 */
15305  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
15306  {
15307  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
15308  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15309  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
15310  int IncMinutes = 0;
15311  NumTrains = TrainDataEntry.NumberOfTrains;
15312  if(ActionVector.empty())
15313  {
15314  continue;
15315  }
15316  if(ActionVector.at(0).SignallerControl)
15317  {
15318  continue;
15319  }
15320  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15321  {
15322  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15323  }
15324 
15325  for(int y = 0; y < NumTrains; y++)
15326  {
15327  if(NumTrains == 1)
15328  {
15329  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
15330  }
15331  else if(y == 0)
15332  {
15333  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
15334  }
15335  else
15336  {
15337  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
15338  }
15339  for(unsigned int z = 0; z < ActionVector.size(); z++)
15340  {
15341  TActionVectorEntry AVE = ActionVector.at(z);
15342  TLSTEntry.AtLocTime = "";
15343  TLSTEntry.ArrTime = "";
15344  TLSTEntry.DepTime = "";
15345  TLSTEntry.Location = "";
15346  TLSTEntry.FrhMarker = "";
15347 
15348  if(AVE.FormatType == StartNew) //Snt only
15349  {
15350  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
15351  {
15352  TLSTEntry.Location = AVE.LocationName;
15353  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
15354  LocServiceTimesVector.push_back(TLSTEntry);
15355 
15356  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15357  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
15358  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15359  {
15360  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15361  {
15362  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
15363  break;
15364  }
15365  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15366  {
15367  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
15368  break;
15369  }
15370  }
15371  if(FoundStopTime == "")
15372  {
15373  throw Exception("Failure to determine FoundStopTime for located Snt");
15374  }
15375  int WhileCount = 0;
15376  while(true)
15377  {
15378  //add minutes until reach FoundStopTime but don't add that time
15379  WhileCount++;
15380  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15381  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15382  TLSTEntry.DepTime = "";
15383  TLSTEntry.ArrTime = "";
15384  if(IncTime >= FoundStopTime) //don't add that time
15385  {
15386  break;
15387  }
15388  LocServiceTimesVector.push_back(TLSTEntry);
15389  if(WhileCount > 2000)
15390  {
15391  throw Exception("While loop failed to break in 2000 loops for located Snt");
15392  }
15393  }
15394  }
15395  else //unlocated Snt, use the EventTime as DepTime for this vector
15396  {
15398  if(TE.ActiveTrackElementName != "")
15399  {
15400  TLSTEntry.Location = TE.ActiveTrackElementName;
15401  }
15402  else
15403  {
15404  int HLoc = TE.HLoc;
15405  int VLoc = TE.VLoc;
15406  AnsiString HString;
15407  AnsiString VString;
15408  if(HLoc < 0)
15409  {
15410  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15411  }
15412  else
15413  {
15414  HString = AnsiString(HLoc);
15415  }
15416  if(VLoc < 0)
15417  {
15418  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15419  }
15420  else
15421  {
15422  VString = AnsiString(VLoc);
15423  }
15424  TLSTEntry.Location = HString + '-' + VString;
15425  }
15426  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
15427  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
15428  LocServiceTimesVector.push_back(TLSTEntry);
15429  }
15430  }
15431 
15432  else if(AVE.SequenceType == Start) //other start entries, all located
15433  {
15434  TLSTEntry.Location = AVE.LocationName;
15435  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
15436  LocServiceTimesVector.push_back(TLSTEntry);
15437  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15438  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
15439  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15440  {
15441  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15442  {
15443  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
15444  break;
15445  }
15446  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15447  {
15448  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
15449  break;
15450  }
15451  }
15452  if(FoundStopTime == "")
15453  {
15454  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
15455  }
15456  int WhileCount = 0;
15457  while(true)
15458  {
15459  //add minutes until reach FoundStopTime but don't add that time
15460  WhileCount++;
15461  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15462  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15463  TLSTEntry.DepTime = "";
15464  TLSTEntry.ArrTime = "";
15465  if(IncTime >= FoundStopTime) //don't add that time
15466  {
15467  break;
15468  }
15469  LocServiceTimesVector.push_back(TLSTEntry);
15470  if(WhileCount > 2000)
15471  {
15472  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
15473  }
15474  }
15475  }
15476 
15477  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if ar5rival add in all mins to the departure or finish
15478  {
15479  TLSTEntry.Location = AVE.LocationName;
15480  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
15481  {
15482  bool SkipAddingMinutes = false;
15483  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
15484  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
15485  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
15486  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
15487  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
15488  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
15489  {
15490  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
15491  {
15492  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
15493  break;
15494  }
15495  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
15496  {
15497  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
15498  if((a == z + 1) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
15499  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
15500  {
15501  LocServiceTimesVector.pop_back();//pop the entry as the linked train will be listed at the relevant time and don't want to list both
15502  SkipAddingMinutes = true;
15503  }
15504  break;
15505  }
15506  }
15507  if(FoundStopTime == "")
15508  {
15509  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
15510  }
15511  if(!SkipAddingMinutes)
15512  {
15513  int WhileCount = 0;
15514  while(true)
15515  {
15516  //add minutes until reach FoundStopTime but don't add that time
15517  WhileCount++;
15518  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15519  TLSTEntry.AtLocTime = IncTime;//all entered times will be AtLocs
15520  TLSTEntry.DepTime = "";
15521  TLSTEntry.ArrTime = "";
15522  if(IncTime >= FoundStopTime) //don't add that time
15523  {
15524  break;
15525  }
15526  LocServiceTimesVector.push_back(TLSTEntry);
15527  if(WhileCount > 2000)
15528  {
15529  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
15530  }
15531  }
15532  }
15533  }
15534  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
15535  {
15536  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
15537  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
15538  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum))//if not it's a new service
15539  {
15540  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
15541  {
15542  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
15543  LocServiceTimesVector.pop_back();
15544  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
15545  }
15546  else //just add the dep & atloc times
15547  {
15548  TLSTEntry.ArrTime = "";
15549  LocServiceTimesVector.push_back(TLSTEntry);
15550  }
15551  }
15552  else //just add the dep & atloc times
15553  {
15554  TLSTEntry.ArrTime = "";
15555  LocServiceTimesVector.push_back(TLSTEntry);
15556  }
15557  }
15558  }
15559 
15560  else if(AVE.FormatType == TimeTimeLoc)
15561  {
15562  TLSTEntry.Location = AVE.LocationName;
15563  if(AVE.ArrivalTime > TDateTime(-1)) //should be
15564  {
15565  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
15566  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
15567  }
15568  if(AVE.DepartureTime > TDateTime(-1)) //should be
15569  {
15570  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
15571  }
15572  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
15573  {
15574  LocServiceTimesVector.push_back(TLSTEntry);
15575  }
15576  else
15577  {
15578  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
15579  TLSTEntry.DepTime = "";
15580  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
15581  TLSTEntry.ArrTime = ""; //done with this now
15582  while(TLSTEntry.AtLocTime < TempDepTime)
15583  {
15584  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15585  if(TLSTEntry.AtLocTime == TempDepTime)
15586  {
15587  TLSTEntry.DepTime = TempDepTime; //restore value
15588  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
15589  }
15590  else
15591  {
15592  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
15593  }
15594  }
15595  }
15596  }
15597 
15598  else if(AVE.FormatType == ExitRailway) //Fer
15599  {
15600  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
15601  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
15602  if(LName != "")
15603  {
15604  TLSTEntry.Location = LName;
15605  }
15606  else
15607  {
15608  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
15609  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
15610  AnsiString HString;
15611  AnsiString VString;
15612  if(HLoc < 0)
15613  {
15614  HString = 'N' + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
15615  }
15616  else
15617  {
15618  HString = AnsiString(HLoc);
15619  }
15620  if(VLoc < 0)
15621  {
15622  VString = 'N' + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
15623  }
15624  else
15625  {
15626  VString = AnsiString(VLoc);
15627  }
15628  TLSTEntry.Location = HString + '-' + VString;
15629  }
15630  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
15631  }
15632 
15633  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
15634  {
15635  AnsiString FrhTime;
15636  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
15637  {
15638  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
15639  }
15640  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
15641  {
15642  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
15643  }
15644  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
15645  TLSTEntry.Location = AVE.LocationName;
15646  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15647  TLSTEntry.FrhMarker = "Frh";
15648  LocServiceTimesVector.push_back(TLSTEntry);
15649  TLSTEntry.FrhMarker = "";
15650  //add all times from next minute to end of timetable
15651  while(IncTime <= LastTTTime)
15652  {
15653  TLSTEntry.AtLocTime = IncTime;
15654  LocServiceTimesVector.push_back(TLSTEntry);
15655  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
15656  }
15657  }
15658 
15659  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
15660  {
15661  if(y == NumTrains - 1)//last repeat, it remains here when accessed for the last train
15662  {
15663  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
15664  TLSTEntry.Location = AVE.LocationName;
15665  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
15666  TLSTEntry.FrhMarker = "Frh";
15667  LocServiceTimesVector.push_back(TLSTEntry);
15668  TLSTEntry.FrhMarker = "";
15669  //add all times from next minute to end of timetable
15670  while(IncTime <= LastTTTime)
15671  {
15672  TLSTEntry.AtLocTime = IncTime;
15673  LocServiceTimesVector.push_back(TLSTEntry);
15674  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
15675  }
15676  }
15677  }
15678 
15679  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
15680  {
15681  //nothing is done here as the entry will be listed at this time under the new service reference
15682  }
15683  }
15684  }
15685  }
15686 
15687  //now sort in location order
15688  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
15689  //LocServiceTimesVector now complete & sorted in location order
15690 
15691  //declare pointers for use in printouts
15692  TLocServiceTimesVector::iterator Ptr1, Ptr2;
15693 
15694  //set up the output file
15695  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15696  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15697 
15698  std::ofstream TTFile3(TTFileName3.c_str());
15699 
15700  if(TTFile3 == 0)
15701  {
15702  ShowMessage("Conflict Analysis file failed to open - can't be created");
15703  Utilities->CallLogPop(2210);
15704  return false;
15705  }
15706 
15707  if(LocServiceTimesVector.empty())
15708  {
15709  ShowMessage("No timetabled services found");
15710  TTFile3.close();
15711  DeleteFile(TTFileName3);
15712  Utilities->CallLogPop(2211);
15713  return false;
15714  }
15715 
15716  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n\n\n";
15717 
15718 
15719  //arrivals
15720  if(ArrChecked)
15721  {
15722  //sort in ArrTime order for each location
15723  Ptr1 = LocServiceTimesVector.begin();
15724  Ptr2 = Ptr1 + 1;
15725  while(Ptr2 != LocServiceTimesVector.end())
15726  {
15727  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
15728  {
15729  Ptr2++;
15730  if(Ptr2 == LocServiceTimesVector.end())
15731  {
15732  break;
15733  }
15734  }
15735  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
15736  Ptr1 = Ptr2; //first entry with next name
15737  if(Ptr2 != LocServiceTimesVector.end())
15738  {
15739  Ptr2++;
15740  }
15741  }
15742 
15743  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
15744 
15745  TTFile3 << "Arrival analysis: an asterisk means that the number of same approach code arrivals is equal to or greater than the number of platforms.\n";
15746  TTFile3 << "If the total number of arrivals exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
15747  MinuteString = " minutes";
15748  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
15749  if(ArrRange == 1)
15750  {
15751  MinuteString = " minute";
15752  }
15753  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
15754  TTFile3 << ",Platforms,Trains\n\n";
15755 
15756  Ptr1 = LocServiceTimesVector.begin();
15757  Ptr2 = Ptr1 + 1;
15758  while(Ptr2 != LocServiceTimesVector.end())
15759  {
15760  PreviousService = "";
15761  NumTrainsAtLoc = 0;
15762  ServiceAndRepeatNumTotal = "";
15763  NumPlats = 0;
15764  NumPlatsAtThisLocCalculated = false;
15765  BasicTime = "";
15766  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
15767  {
15768  PreviousService = "";
15769  NumTrainsAtLoc = 0;
15770  ServiceAndRepeatNumTotal = "";
15771  NumPlats = 0;
15772  NumPlatsAtThisLocCalculated = false;
15773  BasicTime = "";
15774  Ptr1++;
15775  Ptr2++;
15776  if(Ptr2 == LocServiceTimesVector.end())
15777  {
15778  break;
15779  }
15780  }
15781  if(Ptr2 == LocServiceTimesVector.end())
15782  {
15783  break;
15784  }
15785  while(Ptr2->Location == Ptr1->Location)
15786  {
15787  PreviousService = "";
15788  NumTrainsAtLoc = 0;
15789  ServiceAndRepeatNumTotal = "";
15790  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
15791  if((Ptr1->Location == "") && (Ptr2->Location == ""))
15792  {
15793  break;
15794  }
15795  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
15796  {
15797  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
15798  Ptr1++;
15799  Ptr2++;
15800  if(Ptr2 == LocServiceTimesVector.end())
15801  {
15802  break;
15803  }
15804  if(Ptr2->Location != Ptr1->Location)
15805  {
15806  break;
15807  }
15808  }
15809  if(Ptr2 == LocServiceTimesVector.end())
15810  {
15811  break;
15812  }
15813  if(Ptr2->Location != Ptr1->Location)
15814  {
15815  break;
15816  }
15817  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
15818  {
15819  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
15820  {
15821  break;
15822  }
15823  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
15824  {
15825  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
15826  NumPlatsAtThisLocCalculated = true;
15827  }
15828  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
15829  {
15830  if(ServiceAndRepeatNumTotal == "")
15831  {
15832  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
15833  NumTrainsAtLoc = 1;
15834  }
15835  else
15836  {
15837  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
15838  }
15839  }
15840  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
15841  if(ServiceAndRepeatNumTotal == "")
15842  {
15843  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
15844  NumTrainsAtLoc = 1;
15845  }
15846  else
15847  {
15848  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
15849  }
15850  Ptr1 = Ptr2;
15851  Ptr2++;
15852  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
15853  {
15854  int MaxNumberOfSameDirections = 0;
15855  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
15856  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
15857  {
15858 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
15859  TTFile3.close();
15860  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
15861 // Utilities->CallLogPop(2224);
15862 // return false;
15863  }
15864  AnsiString Asterisk = "";
15865  if(MaxNumberOfSameDirections >= NumPlats)
15866  {
15867  Asterisk = "* ";
15868  }
15869  //print out a single line for number of trains at loc with all service refs
15870  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
15871  ArrivalsPrinted = true;
15872  ServiceAndRepeatNumTotal = "";
15873  }
15874  if(Ptr2 == LocServiceTimesVector.end())
15875  {
15876  break;
15877  }
15878  if(Ptr2->Location != Ptr1->Location)
15879  {
15880  break;
15881  }
15882  }
15883  if(Ptr2 == LocServiceTimesVector.end())
15884  {
15885  break;
15886  }
15887  }
15888  }
15889  if(!ArrivalsPrinted)
15890  {
15891  TTFile3 << "Nothing to report for arrivals";
15892  }
15893  TTFile3 << "\n\n\n";
15894  }
15895  //end of routine for arrivals
15896 
15897  //departures
15898  if(DepChecked)
15899  {
15900  //sort in DepTime order for each location
15901  Ptr1 = LocServiceTimesVector.begin();
15902  Ptr2 = Ptr1 + 1;
15903  while(Ptr2 != LocServiceTimesVector.end())
15904  {
15905  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
15906  {
15907  Ptr2++;
15908  if(Ptr2 == LocServiceTimesVector.end())
15909  {
15910  break;
15911  }
15912  }
15913  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
15914  Ptr1 = Ptr2; //first entry with next name
15915  if(Ptr2 != LocServiceTimesVector.end())
15916  {
15917  Ptr2++;
15918  }
15919  }
15920 
15921  //routine for departures - number of trains departing within the specified range with services listed at the end
15922  TTFile3 << "Departure analysis: an asterisk means that the number of same exit code departures is equal to or greater than the number of platforms.\n";
15923  TTFile3 << "If the total number of departures exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
15924  MinuteString = " minutes";
15925  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
15926  if(DepRange == 1)
15927  {
15928  MinuteString = " minute";
15929  }
15930  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
15931  TTFile3 << ",Platforms,Trains\n\n";
15932 
15933  Ptr1 = LocServiceTimesVector.begin();
15934  Ptr2 = Ptr1 + 1;
15935  while(Ptr2 != LocServiceTimesVector.end())
15936  {
15937  PreviousService = "";
15938  NumTrainsAtLoc = 0;
15939  ServiceAndRepeatNumTotal = "";
15940  NumPlats = 0;
15941  NumPlatsAtThisLocCalculated = false;
15942  BasicTime = "";
15943  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
15944  {
15945  PreviousService = "";
15946  NumTrainsAtLoc = 0;
15947  ServiceAndRepeatNumTotal = "";
15948  NumPlats = 0;
15949  NumPlatsAtThisLocCalculated = false;
15950  BasicTime = "";
15951  Ptr1++;
15952  Ptr2++;
15953  if(Ptr2 == LocServiceTimesVector.end())
15954  {
15955  break;
15956  }
15957  }
15958  if(Ptr2 == LocServiceTimesVector.end())
15959  {
15960  break;
15961  }
15962  while(Ptr2->Location == Ptr1->Location)
15963  {
15964  PreviousService = "";
15965  NumTrainsAtLoc = 0;
15966  ServiceAndRepeatNumTotal = "";
15967  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
15968  if((Ptr1->Location == "") && (Ptr2->Location == ""))
15969  {
15970  break;
15971  }
15972  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
15973  {
15974  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
15975  Ptr1++;
15976  Ptr2++;
15977  if(Ptr2 == LocServiceTimesVector.end())
15978  {
15979  break;
15980  }
15981  if(Ptr2->Location != Ptr1->Location)
15982  {
15983  break;
15984  }
15985  }
15986  if(Ptr2 == LocServiceTimesVector.end())
15987  {
15988  break;
15989  }
15990  if(Ptr2->Location != Ptr1->Location)
15991  {
15992  break;
15993  }
15994  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
15995  {
15996  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
15997  {
15998  break;
15999  }
16000  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16001  {
16002  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
16003  NumPlatsAtThisLocCalculated = true;
16004  }
16005  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16006  {
16007  if(ServiceAndRepeatNumTotal == "")
16008  {
16009  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16010  NumTrainsAtLoc = 1;
16011  }
16012  else
16013  {
16014  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
16015  }
16016  }
16017  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
16018  if(ServiceAndRepeatNumTotal == "")
16019  {
16020  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16021  NumTrainsAtLoc = 1;
16022  }
16023  else
16024  {
16025  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
16026  }
16027  Ptr1 = Ptr2;
16028  Ptr2++;
16029  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
16030  {
16031  int MaxNumberOfSameDirections = 0;
16032  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
16033  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
16034  {
16035 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
16036  TTFile3.close();
16037  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
16038 // Utilities->CallLogPop(2225);
16039 // return false;
16040  }
16041  AnsiString Asterisk = "";
16042  if(MaxNumberOfSameDirections >= NumPlats)
16043  {
16044  Asterisk = "* ";
16045  }
16046  //print out a single line for number of trains at loc with all service refs
16047  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
16048  DeparturesPrinted = true;
16049  ServiceAndRepeatNumTotal = "";
16050  }
16051  if(Ptr2 == LocServiceTimesVector.end())
16052  {
16053  break;
16054  }
16055  if(Ptr2->Location != Ptr1->Location)
16056  {
16057  break;
16058  }
16059  }
16060  if(Ptr2 == LocServiceTimesVector.end())
16061  {
16062  break;
16063  }
16064  }
16065  }
16066  if(!DeparturesPrinted)
16067  {
16068  TTFile3 << "Nothing to report for departures";
16069  }
16070  TTFile3 << "\n\n\n";
16071  }
16072  //end of routine for departures
16073 
16074 
16075  //list trains at locations at same time
16076 
16077  if(AtLocChecked)
16078  {
16079  //sort in AtLocTime order for each location
16080  Ptr1 = LocServiceTimesVector.begin();
16081  Ptr2 = Ptr1 + 1;
16082  while(Ptr2 != LocServiceTimesVector.end())
16083  {
16084  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
16085  {
16086  Ptr2++;
16087  if(Ptr2 == LocServiceTimesVector.end())
16088  {
16089  break;
16090  }
16091  }
16092  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
16093  Ptr1 = Ptr2; //first entry with next name
16094  if(Ptr2 != LocServiceTimesVector.end())
16095  {
16096  Ptr2++;
16097  }
16098  }
16099 
16100  //print out simultaneous AtLocs (don't need range of times for AtLocs)
16101  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
16102  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
16103  TTFile3 << ",Platforms,Trains,\n\n";
16104  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
16105  Ptr1 = LocServiceTimesVector.begin();
16106  Ptr2 = Ptr1 + 1;
16107  while(Ptr2 != LocServiceTimesVector.end())
16108  {
16109  PreviousService = "";
16110  ServiceAndRepeatNumTotal = "";
16111  NumTrainsAtLoc = 0;
16112  NumPlats = 0;
16113  NumPlatsAtThisLocCalculated = false;
16114  FrhCount = 0;
16115  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
16116  {
16117  PreviousService = "";
16118  ServiceAndRepeatNumTotal = "";
16119  NumTrainsAtLoc = 0;
16120  NumPlats = 0;
16121  NumPlatsAtThisLocCalculated = false;
16122  FrhCount = 0;
16123  Ptr1++;
16124  Ptr2++;
16125  if(Ptr2 == LocServiceTimesVector.end())
16126  {
16127  break;
16128  }
16129  }
16130  if(Ptr2 == LocServiceTimesVector.end())
16131  {
16132  break;
16133  }
16134  while(Ptr2->Location == Ptr1->Location)
16135  {
16136  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
16137  {
16138  FrhCount++;
16139  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16140  }
16141  PreviousService = "";
16142  NumTrainsAtLoc = 0;
16143  ServiceAndRepeatNumTotal = "";
16144  if((Ptr1->Location == "") && (Ptr2->Location == ""))
16145  {
16146  break;
16147  }
16148  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
16149  {
16150  Ptr1++;
16151  if(Ptr1->FrhMarker == "Frh")
16152  {
16153  FrhCount++;
16154  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16155  }
16156  Ptr2++;
16157  if(Ptr2 == LocServiceTimesVector.end())
16158  {
16159  break;
16160  }
16161  if(Ptr2->Location != Ptr1->Location)
16162  {
16163  break;
16164  }
16165  }
16166  if(Ptr2 == LocServiceTimesVector.end())
16167  {
16168  break;
16169  }
16170  if(Ptr2->Location != Ptr1->Location)
16171  {
16172  break;
16173  }
16174  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
16175  {
16176  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
16177  {
16178  break;
16179  }
16180  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
16181  {
16182  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
16183  NumPlatsAtThisLocCalculated = true;
16184  }
16185  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
16186  {
16187  if(ServiceAndRepeatNumTotal == "")
16188  {
16189  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
16190  NumTrainsAtLoc = 1;
16191  }
16192  else
16193  {
16194  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
16195  }
16196  }
16197  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
16198  if(ServiceAndRepeatNumTotal == "")
16199  {
16200  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
16201  NumTrainsAtLoc = 1;
16202  }
16203  else
16204  {
16205  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
16206  }
16207  Ptr1 = Ptr2;
16208  if(Ptr1->FrhMarker == "Frh")
16209  {
16210  FrhCount++;
16211  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
16212  }
16213  Ptr2++;
16214  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
16215  {
16216 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
16217 //new text //don't print out if all remainers or if only 1 train at loc
16218  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
16219 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
16220 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
16221  {
16222  AnsiString Asterisk = "";
16223  if(NumTrainsAtLoc > NumPlats)
16224  {
16225  Asterisk = "* ";
16226  }
16227  //print out a single line for number of trains at loc with all service refs
16228  if(FrhCount == 0)
16229  {
16230  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
16231  }
16232  else if(FrhCount == 1)
16233  {
16234  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
16235  }
16236  else
16237  {
16238  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
16239  }
16240  LastFrhCount = FrhCount;
16241  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
16242  AtLocsPrinted = true;
16243  ServiceAndRepeatNumTotal = "";
16244  }
16245  }
16246  if(Ptr2 == LocServiceTimesVector.end())
16247  {
16248  break;
16249  }
16250  if(Ptr2->Location != Ptr1->Location)
16251  {
16252  break;
16253  }
16254  }
16255  if(Ptr2 == LocServiceTimesVector.end())
16256  {
16257  break;
16258  }
16259  }
16260  }
16261  if(!AtLocsPrinted)
16262  {
16263  TTFile3 << "Nothing to report for trains at locations";
16264  }
16265  TTFile3 << "\n\n\n";
16266  //end of simultaneous AtLocs
16267 
16268 /*
16269  //print out the full vector here for testing purposes
16270  TTFile3 << "Full LocServiceTimesVector\n\n";
16271  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
16272 
16273  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
16274  {
16275  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
16276  }
16277 
16278  TTFile3 << "\n\n\n";
16279 */
16280  }
16281 
16282  TTFile3.close();
16283  Utilities->CallLogPop(2212);
16284  return true;
16285  }
16286 
16287  catch(const Exception &e)
16288  {
16289  AnsiString TTErrorFileName = "Analysis Error.txt";
16290  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
16291  std::ofstream TTError(TTErrorFileName.c_str());
16292  if(TTError == 0)
16293  {
16294  ShowMessage("Analysis error file failed to open - can't be created");
16295  Utilities->CallLogPop(2233);
16296  return false;
16297  }
16298  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16299  TTError << TimeNow.c_str() << "\n" << ArrRange << "\n" << ArrChecked << "\n" << DepRange << "\n" <<
16300  DepChecked << "\n" << AtLocChecked << "\n" << AnsiString(e.Message);
16301  TTError.close();
16302  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
16303  Utilities->CallLogPop(2226);
16304  return false;
16305  }
16306 }
16307 
16308 // ---------------------------------------------------------------------------
16309 
16310 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
16311 {
16312 //convert times to integer minutes
16313  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
16314  if((Time1 == "") || (Time2 == ""))
16315  {
16316  Utilities->CallLogPop(2213);
16317  return false;
16318  }
16319  int Mins = Time1.SubString(4,2).ToInt();
16320  int Hours = Time1.SubString(1,2).ToInt();
16321  int Time1Mins = (Hours * 60) + Mins;
16322  Mins = Time2.SubString(4,2).ToInt();
16323  Hours = Time2.SubString(1,2).ToInt();
16324  int Time2Mins = (Hours * 60) + Mins;
16325  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
16326  {
16327  Utilities->CallLogPop(2214);
16328  return true;
16329  }
16330  Utilities->CallLogPop(2215);
16331  return false;
16332 }
16333 
16334 // ---------------------------------------------------------------------------
16335 
16336 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
16337  bool &AnalysisError, int &MaxNumberOfSameDirections)
16338 {//input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
16339 
16340  try
16341  {
16342  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
16343  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
16344  int SCPos = 0;
16345  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
16346  //first change every second comma in Input to a semicolon so can separate services but keep times with services
16347  bool EvenComma = false;
16348  for(int x = 1; x <= Input.Length(); x++)
16349  {
16350  TempStr1 = Input[x];
16351  if(TempStr1 == AnsiString(',') && EvenComma)
16352  {
16353  TempStr2 += ';';
16354  }
16355  else
16356  {
16357  TempStr2 += Input[x];
16358  }
16359  if(TempStr1 == AnsiString(','))
16360  {
16361  EvenComma = !EvenComma;
16362  }
16363  }
16364  //load up the list of services with associated times
16365  while(TempStr2.Length() > 0)
16366  {
16367  SCPos = TempStr2.Pos(';');
16368  if(SCPos > 0) //0 if not found, as won't be when only one service left
16369  {
16370  OneService = TempStr2.SubString(1, SCPos - 1);
16371  ServiceList.push_back(OneService);
16372  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
16373  }
16374  else //no semicolon so looking at last (or only) element
16375  {
16376  ServiceList.push_back(TempStr2);
16377  TempStr2 = "";
16378  }
16379  }
16380  ServiceList.sort(); // alphabetical order
16381  ServiceList.unique(); //remove duplicates
16382  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
16383 
16384  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
16385  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
16386  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
16387  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
16388 
16389  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16390  {
16391  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
16392  }
16393  SLIt3 = ServiceList.end();
16394  SLIt3--; //so points to last element
16395  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
16396  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
16397  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
16398  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
16399  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
16400  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
16401 
16402  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
16403  {
16404  SLIt = SLIt1;
16405  SLIt++; //so points to one after SLIt1
16406  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
16407  {
16408  continue; //already allocated so skip to the next
16409  }
16410  else
16411  {
16412  CommaPos1 = SLIt1->Pos(','); //can't be 0
16413  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
16414  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
16415  SpacePos = ServiceRef1.Pos(' ');
16416  RepeatNum1 = 0;
16417  if(SpacePos > 0) //otherwise it's already correct
16418  {
16419  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
16420  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
16421  if(RepeatInfo1[1] == 'F')
16422  {
16423  RepeatNum1 = 0;
16424  }
16425  else
16426  {
16427  SpacePos = RepeatInfo1.Pos(' ');
16428  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
16429  }
16430  }
16431  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
16432  //but this includes the "&0" etc so need to strip these
16433  AmpersandPos = AnsiTime1.Pos('&');
16434  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
16435 
16436  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
16437  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
16438  {
16439  throw Exception("ASCLIt1 Error in " + Input);
16440  }
16441  ServiceCallingLocsList1 = ASCLIt1->second;
16442  AmpersandPos = SLIt1->Pos('&');
16443  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16444  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
16445 
16446  SameDirectionCount = 1;
16447  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
16448  {
16449  CommaPos2 = SLIt2->Pos(','); //can't be 0
16450  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
16451  //but this contains "(First service..." etc so need to strip these
16452  SpacePos = ServiceRef2.Pos(' ');
16453  RepeatNum2 = 0;
16454  if(SpacePos > 0) //otherwise it's already correct
16455  {
16456  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
16457  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
16458  if(RepeatInfo2[1] == 'F')
16459  {
16460  RepeatNum2 = 0;
16461  }
16462  else
16463  {
16464  SpacePos = RepeatInfo2.Pos(' ');
16465  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
16466  }
16467 
16468  }
16469  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
16470  //but this includes the "&0" etc so need to strip these
16471  AmpersandPos = AnsiTime2.Pos('&');
16472  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
16473 
16474  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
16475  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
16476  {
16477  throw Exception("ASCLIt2 Error in " + Input);
16478  }
16479  ServiceCallingLocsList2 = ASCLIt2->second;
16480  //now compare the two
16481  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
16482  {
16483  int AmpersandPos = SLIt2->Pos('&');
16484  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16485  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
16486  SameDirectionCount++;
16487  }
16488  }
16489  if(SameDirectionCount > MaxNumberOfSameDirections)
16490  {
16491  MaxNumberOfSameDirections = SameDirectionCount;
16492  }
16493  }
16494  }
16495 
16496  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0"))//*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
16497  { //any existing direction so allocate it now
16498  AmpersandPos = SLIt3->Pos('&');
16499  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
16500  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
16501  }
16502 
16503  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
16504  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16505  {
16506  //extract the DirectionMarker as an integer
16507  AmpersandPos = SLIt->Pos('&');
16508  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
16509  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
16510  DirectionMarker = DirectionMarkerString.ToInt();
16511  AnsiString DirectionSuffix = "";
16512  char c;
16513  if(DirectionMarker < 27)
16514  {
16515  c = 64 + DirectionMarker; //so 1 -> 'A'
16516  DirectionSuffix = "," + AnsiString(c);
16517  }
16518  else if(DirectionMarker < 53)
16519  {
16520  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
16521  DirectionSuffix = ",A" + AnsiString(c);
16522  }
16523  else
16524  {
16525  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
16526  }
16527  *SLIt = ServiceWithoutMarker + DirectionSuffix;
16528  }
16529  //now prepare the final consolidated output
16530  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16531  {
16532  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
16533  }
16534  if(Output.Length() > 0)
16535  {
16536  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
16537  }
16538  Utilities->CallLogPop(2216);
16539  return Output;
16540  }
16541 
16542  catch(const Exception &e)
16543  {
16544  AnalysisError = true;
16545  Utilities->CallLogPop(2227);
16546  return e.Message;
16547  }
16548 }
16549 
16550 // ---------------------------------------------------------------------------
16551 
16552 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
16553 {//similar to above but doesn't include times in the input
16554  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
16555  AnsiString InternalInput = Input, Output = "", OneService = "";
16556  int CommaPos = 0;
16557  std::list<AnsiString> ServiceList;
16558  //load up the list
16559  while(InternalInput.Length() > 0)
16560  {
16561  CommaPos = InternalInput.Pos(',');
16562  if(CommaPos > 0) //0 if not found, as won't be when only one service left
16563  {
16564  OneService = InternalInput.SubString(1, CommaPos - 1);
16565  ServiceList.push_back(OneService);
16566  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
16567  }
16568  else //no comma so looking at last (or only) element
16569  {
16570  ServiceList.push_back(InternalInput);
16571  InternalInput = "";
16572  }
16573  }
16574 
16575  ServiceList.sort(); // alphabetical order
16576  ServiceList.unique(); //remove duplicates
16577  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
16578  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
16579  {
16580  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
16581  }
16582  if(Output.Length() > 0)
16583  {
16584  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
16585  }
16586  Utilities->CallLogPop(2217);
16587  return Output;
16588 }
16589 
16590 // ---------------------------------------------------------------------------
16591 
16592 
16593 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
16594  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
16595  {
16596  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
16597  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
16598 
16599  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
16600  //general list pointers, LocPtrs point to Location in the two lists
16601 
16602  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
16603  //for List1
16604  bool LocFound = false;
16605  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
16606  int IncMinutes;
16607  TDateTime FirstServiceTime;
16608 
16609  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
16610  int Ref1Target = 0, Ref1Count = 0;
16611  int SlashPos = Ref1.Pos('/');
16612  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
16613  {
16614  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
16615  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
16616  }
16617  int Ref2Target = 0, Ref2Count = 0;
16618  SlashPos = Ref2.Pos('/');
16619  if(SlashPos > 0) //if 0 leave as is
16620  {
16621  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
16622  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
16623  }
16624 
16625  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
16626  { //even if others have same names. But if there are cdt's then need to refind the correct service
16627  if((*ListPtr1) == Location) //
16628  {
16629  LocPtr1 = ListPtr1; //may be modified later
16630  LocFound = true;
16631  }
16632  if(ListPtr1->SubString(1, 3) == "%%%")
16633  {
16634  AnsiString CDTTime = ListPtr1->SubString(4, 5);
16635  //now adjust the time to correspond to the repeat if there is one
16636  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
16637  {
16638  IncMinutes = -1;
16639  FirstServiceTime = TDateTime(-1);
16640  bool BreakFlag = false;
16641  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
16642  {
16643  if(TDVIt->ServiceReference == Ref1)
16644  {
16645  if(Ref1Target > Ref1Count)
16646  {
16647  Ref1Count++;
16648  continue;
16649  }
16650  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
16651  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
16652  {
16653  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
16654  {
16655  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
16656  BreakFlag = true;
16657  break;
16658  }
16659  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
16660  {
16661  FirstServiceTime = AVIt->ArrivalTime;
16662  BreakFlag = true;
16663  break;
16664  }
16665  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
16666  {
16667  FirstServiceTime = AVIt->DepartureTime;
16668  BreakFlag = true;
16669  break;
16670  }
16671  }
16672  if(BreakFlag)
16673  {
16674  break;
16675  }
16676  }
16677  }
16678  if(IncMinutes == -1)
16679  {
16680  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16681  }
16682  if(FirstServiceTime == TDateTime(-1))
16683  {
16684  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16685  }
16686  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
16687  }
16688  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
16689  {
16690  LocFound = false;
16691  continue;
16692  }
16693  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
16694  {
16695  break;
16696  }
16697  if(Time1 > CDTTime) //not there yet so go on
16698  {
16699  LocFound = false;
16700  continue;
16701  }
16702  if(Time1 < CDTTime) //gone too far so can stop now
16703  {
16704  break;
16705  }
16706  }
16707  }
16708  if(!LocFound) //have to find it in both lists
16709  {
16710  Utilities->CallLogPop(2228);
16711  return false;
16712  }
16713 
16714  //for List2
16715  LocFound = false;
16716  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
16717  {
16718  if((*ListPtr2) == Location)
16719  {
16720  LocPtr2 = ListPtr2; //may be modified later
16721  LocFound = true;
16722  }
16723  if(ListPtr2->SubString(1, 3) == "%%%")
16724  {
16725  AnsiString CDTTime = ListPtr2->SubString(4, 5);
16726  //now adjust the time to correspond to the repeat if there is one
16727  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
16728  {
16729  IncMinutes = -1;
16730  FirstServiceTime = TDateTime(-1);
16731  bool BreakFlag = false;
16732  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
16733  {
16734  if(TDVIt->ServiceReference == Ref2)
16735  {
16736  if(Ref2Target > Ref2Count)
16737  {
16738  Ref2Count++;
16739  continue;
16740  }
16741  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
16742  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
16743  {
16744  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
16745  {
16746  FirstServiceTime = AVIt->EventTime;
16747  BreakFlag = true;
16748  break;
16749  }
16750  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
16751  {
16752  FirstServiceTime = AVIt->ArrivalTime;
16753  BreakFlag = true;
16754  break;
16755  }
16756  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
16757  {
16758  FirstServiceTime = AVIt->DepartureTime;
16759  BreakFlag = true;
16760  break;
16761  }
16762  }
16763  if(BreakFlag)
16764  {
16765  break;
16766  }
16767  }
16768  }
16769  if(IncMinutes == -1)
16770  {
16771  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16772  }
16773  if(FirstServiceTime == TDateTime(-1))
16774  {
16775  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
16776  }
16777  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
16778  }
16779  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
16780  {
16781  LocFound = false;
16782  continue;
16783  }
16784  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
16785  {
16786  break;
16787  }
16788  if(Time2 > CDTTime) //not there yet so go on
16789  {
16790  LocFound = false;
16791  continue;
16792  }
16793  if(Time2 < CDTTime) //gone too far so can stop now
16794  {
16795  break;
16796  }
16797  }
16798  }
16799  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
16800  {
16801  Utilities->CallLogPop(2229);
16802  return false;
16803  }
16804 
16805  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
16806  //set ListPtr1 to the search start position
16807  if(Arrival)
16808  {
16809  LP1 = List1.begin();
16810  LP1--; //now points to before the first entry
16811  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
16812  {
16813  if(ListPtr1 == List1.begin())
16814  {
16815  break;
16816  }
16817  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
16818  {
16819  ListPtr1++; //point to one past the cdt
16820  break;
16821  }
16822  }
16823  //set ListPtr2 to the search start position
16824  LP2 = List2.begin();
16825  LP2--; //now points to before the first entry
16826  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
16827  {
16828  if(ListPtr2 == List2.begin())
16829  {
16830  break;
16831  }
16832  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
16833  {
16834  ListPtr2++; //point to one past the cdt
16835  break;
16836  }
16837  }
16838  //ListPtr1 & 2 now at search start position
16839  LP1 = ListPtr1;
16840  LP2 = ListPtr2;
16841  //now search forwards, i.e. for common locations before Location
16842  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
16843  {
16844  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
16845  {
16846  break;
16847  }
16848  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
16849  {
16850  break;
16851  }
16852  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
16853  {
16854  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
16855  {
16856  break;
16857  }
16858  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
16859  {
16860  break;
16861  }
16862  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
16863  {
16864  Utilities->CallLogPop(2230);
16865  return true;
16866  }
16867  }
16868  }
16869  }
16870 
16871  //now, for the departure analysis, reset the start positions and search locations after Location
16872 
16873  else
16874  {
16875  LP1 = LocPtr1; LP1++; //start at one past the location itself
16876  LP2 = LocPtr2; LP2++;
16877  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
16878  {
16879  if(ListPtr1 == List1.end()) //reached end point so stop
16880  {
16881  break;
16882  }
16883  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
16884  {
16885  break;
16886  }
16887  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
16888  {
16889  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
16890  {
16891  break;
16892  }
16893  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
16894  {
16895  break;
16896  }
16897  if((*ListPtr1) == (*ListPtr2)) //found a common later location
16898  {
16899  Utilities->CallLogPop(2231);
16900  return true;
16901  }
16902  }
16903  }
16904  }
16905  Utilities->CallLogPop(2232);
16906  return false;
16907  }
16908 
16909 // ---------------------------------------------------------------------------
16910 
16911 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
16912 {
16913  // changed at v2.7.0 to show allowable exit elements
16914  if(ExitList.empty())
16915  return "";
16916 
16917  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
16918  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
16919  AnsiString ExitLocList = "";
16920  AllowedExits = "";
16921 
16922  unsigned int Counter = 0;
16923  for(TExitListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
16924  {
16925  ExitLocList += Track->TrackElementAt(7777, *ELIt).ElementID + " ";
16926  Counter++;
16927  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
16928  {
16929  ExitLocList += "\n";
16930  }
16931  }
16932  if(StartName == "")
16933  {
16934  if(ExitList.size() == 1)
16935  {
16936  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
16937  Utilities->CallLogPop(1571);
16938  return " at " + ID;
16939  }
16940  else
16941  {
16942  Utilities->CallLogPop(1572);
16943  if(ExitList.size() < 4)
16944  {
16945  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
16946  return "";
16947  }
16948  else
16949  {
16950  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
16951  return "";
16952  }
16953  }
16954  }
16955  for(TExitListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
16956  {
16957  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
16958  {
16959  Utilities->CallLogPop(1570);
16960  if(ExitList.size() < 4)
16961  {
16962  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
16963  return "";
16964  }
16965  else
16966  {
16967  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
16968  return "";
16969  }
16970  }
16971  }
16972  Utilities->CallLogPop(1569);
16973  if(ExitList.size() < 4)
16974  {
16975  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
16976  return " at " + StartName;
16977  }
16978  else
16979  {
16980  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
16981  return " at " + StartName;
16982  }
16983 }
16984 
16985 // ---------------------------------------------------------------------------
16986 /* can't trust this as locations within a vector may not be contiguous
16987  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
16988  {
16989  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
16990  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
16991  //must be preceded by a TimeLoc departure
16992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
16993  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
16994  {
16995  if((AVPtr + x) < TDEPtr->ActionVector.end())
16996  {
16997  AnsiString xx = (AVPtr + x)->Command;//test
16998  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
16999  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
17000  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
17001  {
17002  Utilities->CallLogPop();
17003  return false;
17004  }
17005  else if((AVPtr + x)->SequenceType == Finish)
17006  {
17007  Utilities->CallLogPop();
17008  return true;
17009  }
17010  }
17011  }
17012  Utilities->CallLogPop();
17013  return false;
17014  }
17015 */
17016 // ---------------------------------------------------------------------------
17017 
17018 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
17019 {
17020  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
17021  AnsiString FormatStr = "####0.0";
17022  AnsiString AvLateArrMins = "";
17023  AnsiString AvEarlyArrMins = "";
17024  AnsiString AvLatePassMins = "";
17025  AnsiString AvEarlyPassMins = "";
17026  AnsiString AvLateDepMins = "";
17027 
17028  if(LateArrivals > 0)
17029  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
17030  if(EarlyArrivals > 0)
17031  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
17032  if(LatePasses > 0)
17033  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
17034  if(EarlyPasses > 0)
17035  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
17036  if(LateDeps > 0)
17037  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
17038 
17039  PerfFile << '\n' << '\n' << "***************************************";
17040  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
17041 
17042  if(OnTimeArrivals != 1)
17043  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
17044  else
17045  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
17046 
17047  if(LateArrivals > 1)
17048  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
17049  else if(LateArrivals == 1)
17050  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
17051  else
17052  PerfFile << LateArrivals << " late arrivals" << '\n';
17053 
17054  if(EarlyArrivals > 1)
17055  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
17056  else if(EarlyArrivals == 1)
17057  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
17058  else
17059  PerfFile << EarlyArrivals << " early arrivals" << '\n';
17060 
17061  if(OnTimePasses != 1)
17062  PerfFile << OnTimePasses << " on-time passes" << '\n';
17063  else
17064  PerfFile << OnTimePasses << " on-time pass" << '\n';
17065 
17066  if(LatePasses > 1)
17067  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
17068  else if(LatePasses == 1)
17069  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
17070  else
17071  PerfFile << LatePasses << " late passes" << '\n';
17072 
17073  if(EarlyPasses > 1)
17074  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
17075  else if(EarlyPasses == 1)
17076  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
17077  else
17078  PerfFile << EarlyPasses << " early passes" << '\n';
17079 
17080  if(OnTimeDeps != 1)
17081  PerfFile << OnTimeDeps << " on-time departures" << '\n';
17082  else
17083  PerfFile << OnTimeDeps << " on-time departure" << '\n';
17084 
17085  if(LateDeps > 1)
17086  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
17087  else if(LateDeps == 1)
17088  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
17089  else
17090  PerfFile << LateDeps << " late departures" << '\n';
17091 
17092  TDateTime TempExcessLCDownTime;
17093  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
17094  {
17095  if(Track->BarriersDownVector.at(x).ReducedTimePenalty)
17096  {
17097  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
17098  }
17099  else
17100  {
17101  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
17102  }
17103  if(TempExcessLCDownTime > TDateTime(0))
17104  {
17105  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
17106  }
17107  }
17108 
17109  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
17110 
17111  if(ExcessLCDownMins > 0.1)
17112  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
17113 
17114  if(MissedStops != 1)
17115  PerfFile << MissedStops << " missed stops" << '\n';
17116  else
17117  PerfFile << MissedStops << " missed stop" << '\n';
17118 
17119  if(OtherMissedEvents != 1)
17120  PerfFile << OtherMissedEvents << " other missed events" << '\n';
17121  else
17122  PerfFile << OtherMissedEvents << " other missed event" << '\n';
17123 
17124  if(UnexpectedExits != 1)
17125  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
17126  else
17127  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
17128 
17129  if(IncorrectExits != 1)
17130  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
17131  else
17132  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
17133 
17134  if(NumFailures != 1)
17135  PerfFile << NumFailures << " train failures" << '\n';
17136  else
17137  PerfFile << NumFailures << " train failure" << '\n';
17138 
17139  if(AvHoursIntValue > 0)
17140  {
17141  if(AvHoursIntValue == 1)
17142  {
17143  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
17144  }
17145  else
17146  {
17147  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
17148  }
17149  }
17150 
17151  AnsiString AvLateMinsLocsNotReached = "";
17152 
17154  int LocsNotReached = (NotStartedTrainArrDep + OperatingTrainArrDep) / 2;
17155 
17156  // each location has an arrival and departure (generally) so divide by 2
17157  if(LocsNotReached > 0)
17158  {
17159  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainArrDep + OperatingTrainArrDep));
17160  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
17161  }
17162 
17163  if(SPADRisks != 1)
17164  PerfFile << SPADRisks << " SPAD risks" << '\n';
17165  else
17166  PerfFile << SPADRisks << " SPAD risk" << '\n';
17167 
17168  if(SPADEvents != 1)
17169  PerfFile << SPADEvents << " SPADs" << '\n';
17170  else
17171  PerfFile << SPADEvents << " SPAD" << '\n';
17172 
17173  if(Derailments != 1)
17174  PerfFile << Derailments << " derailments" << '\n';
17175  else
17176  PerfFile << Derailments << " derailment" << '\n';
17177 
17178  if(CrashedTrains != 1)
17179  PerfFile << CrashedTrains << " crashed trains" << '\n';
17180  else
17181  PerfFile << CrashedTrains << " crashed train" << '\n';
17182 
17183  PerfFile << '\n' << "***************************************" << '\n';
17184 
17185  bool DerailSPADFlag = false, CrashFlag = false;
17186 
17187  int OverallScorePercent = 100;
17188  int TotArrDep = 0;
17189  double TotLateMinsFactor = 1;
17190  double MissedStopAndSPADRiskFactor = 1;
17191  double NetNegFactor = 1;
17192 
17194  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
17195  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
17196  // 'no timetabled departures... message, which was inappropriate
17197 
17198  if((SPADEvents > 0) || (Derailments > 0))
17199  {
17200  OverallScorePercent = 5; // overrides other calculations
17201  DerailSPADFlag = true;
17202  }
17203  if(CrashedTrains > 0)
17204  {
17205  OverallScorePercent = 0; // overrides other calculations
17206  CrashFlag = true;
17207  }
17208  if(OverallScorePercent == 100)
17209  {
17210  if(TotArrDep > 0)
17211  {
17212  TotLateMinsFactor =
17214  ((OtherMissedEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDep);
17215  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
17216  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
17217  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDep);
17218  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
17219  // of arrivals & departures, where 4% = half, 8% = a quarter etc
17220  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
17221  // NetNegfactor: product of the above two
17222  OverallScorePercent = 100 * NetNegFactor;
17223  }
17224  }
17225  if((TotArrDep > 0) || DerailSPADFlag || CrashFlag)
17226  // flag condits added at v1.1.4 - see above for what the error was
17227  {
17228  AnsiString OneFailureString = ", though the failure would account for some poor performance";
17229  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
17230  AnsiString AddedString = "";
17231  if(NumFailures == 1)
17232  AddedString = OneFailureString;
17233  if(NumFailures > 1)
17234  AddedString = TwoOrMoreFailureString;
17235  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
17236  AnsiString Rating = "";
17237  if(OverallScorePercent == 100)
17238  Rating = "Perfect!";
17239  else if(OverallScorePercent >= 95)
17240  Rating = "Excellent";
17241  else if(OverallScorePercent >= 90)
17242  Rating = "Very good";
17243  else if(OverallScorePercent >= 80)
17244  Rating = "Good";
17245  else if(OverallScorePercent >= 70)
17246  Rating = "Fair";
17247  else if(OverallScorePercent >= 60)
17248  Rating = "Unacceptable" + AddedString;
17249  else if(OverallScorePercent >= 50)
17250  Rating = "Poor" + AddedString;
17251  else if(OverallScorePercent >= 40)
17252  Rating = "Bad" + AddedString;
17253  else if(OverallScorePercent >= 30)
17254  Rating = "Very bad" + AddedString;
17255  else if(OverallScorePercent >= 20)
17256  Rating = "Terrible" + AddedString;
17257  else if(OverallScorePercent >= 10)
17258  Rating = "Appalling" + AddedString;
17259  else if(OverallScorePercent >= 5)
17260  {
17261  if(DerailSPADFlag)
17262  Rating = "Disastrous - potential loss of life";
17263  // SPADs/Derailments
17264  else
17265  Rating = "Dire" + AddedString;
17266  }
17267  else if(OverallScorePercent < 5)
17268  {
17269  if(CrashFlag)
17270  Rating = "Catastrophic - loss of life"; // Crashes
17271  else
17272  Rating = "Abysmal";
17273  }
17274  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
17275  }
17276  else
17277  {
17278  PerfFile << "\nThere were no timetabled departures or arrivals so there is insufficient information to provide a performance score or rating" << '\n';
17279  }
17280  PerfFile << '\n' << "***************************************";
17281  Utilities->CallLogPop(1736);
17282 }
17283 
17284 // ---------------------------------------------------------------------------
17285 
17287 {
17288  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
17289  for(unsigned int x = 0; x < TrainVector.size(); x++)
17290  {
17291  TTrain &Train = TrainVectorAt(58, x);
17292  if(Train.Crashed)
17293  // can't use background colours for crashed & derailed because same colour
17294  {
17295  CrashWarning = true;
17296  }
17297  else if(Train.Derailed)
17298  // can't use background colours for crashed & derailed because same colour
17299  {
17300  DerailWarning = true;
17301  }
17302  else if(Train.BackgroundColour == clSPADBackground)
17303  // use colour as that changes as soon as passes signal
17304  {
17305  SPADWarning = true;
17306  }
17307  else if(Train.BackgroundColour == clTrainFailedBackground)
17308  {
17309  TrainFailedWarning = true;
17310  }
17311  else if(Train.BackgroundColour == clCallOnBackground)
17312  // use colour as also stopped at signal
17313  {
17314  CallOnWarning = true;
17315  }
17316  else if(Train.BackgroundColour == clSignalStopBackground)
17317  // use colour to distinguish from call-on
17318  {
17319  SignalStopWarning = true;
17320  }
17321  else if(Train.BackgroundColour == clBufferAttentionNeeded)
17322  // use colour to distinguish from ordinary buffer stop
17323  {
17324  BufferAttentionWarning = true;
17325  }
17326  }
17327  Utilities->CallLogPop(1796);
17328 }
17329 
17330 // ---------------------------------------------------------------------------
17331 
17333 {
17334  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
17335 
17336  // calculate lateness for running trains
17339  for(unsigned int x = 0; x < TrainVector.size(); x++)
17340  {
17341  TTrain &Train = TrainVectorAt(64, x);
17342  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
17343  AVEntryPtr++)
17344  {
17345  if(AVEntryPtr < Train.ActionVectorEntryPtr)
17346  continue;
17347  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
17348  TTClockTime))
17349  {
17350  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
17352  }
17353  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
17354  TTClockTime))
17355  {
17356  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber,
17357  Train.IncrementalMinutes));
17359  }
17360  }
17361  }
17362 
17363  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
17366 
17367  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17368  {
17369  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
17370  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
17371  int IncrementalMinutes = 0;
17372  if(AVEntryLast.FormatType == Repeat)
17373  {
17374  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
17375  }
17376  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
17377  {
17378  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
17379  if(TTOD.RunningEntry != NotStarted)
17380  continue;
17381  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
17382  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
17383  bool TrainOperatingFlag = false;
17384  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
17385  {
17386  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
17387  {
17388  TrainOperatingFlag = true;
17389  break;
17390  }
17391  }
17392  if(TrainOperatingFlag)
17393  continue;
17394 
17395  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
17396  {
17397  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
17398  }
17399  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
17400  {
17401  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
17402  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
17403  {
17404  break; // all the rest will also be greater (& default of -1 will be less)
17405  }
17406  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
17407  {
17408  break; // all the rest will also be greater (& default of -1 will be less)
17409  }
17410  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
17411  {
17412  break; // all the rest will also be greater (& default of -1 will be less)
17413  }
17414  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
17415  {
17416  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
17418  }
17419  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
17420  {
17421  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
17423  }
17424  }
17425  }
17426  }
17427  Utilities->CallLogPop(1894);
17428 }
17429 
17430 // ---------------------------------------------------------------------------
17431 
17433  // new v2.2.0 for OperatorActionPanel
17434  // clears entries then adds values for running trains then for continuation entries
17435  // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
17436 {
17437  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
17438  OpTimeToActMultiMap.clear();
17439  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
17440 
17441  if(!TrainVector.empty())
17442  // build OpTimeToActMultiMap entries for running trains
17443  {
17444  AnsiString HeadCode;
17445  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
17446  int TrainID;
17447  THCandTrainPosParam HCandTrainPosParam;
17448  for(unsigned int x = 0; x < TrainVector.size(); x++)
17449  {
17450  HeadCode = TrainVectorAt(62, x).HeadCode;
17451  TrainID = TrainVectorAt(63, x).TrainID;
17452  HCandTrainPosParam.first = HeadCode;
17453  HCandTrainPosParam.second = TrainID;
17454  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
17455  if((TimeToAct >= 0) && (TimeToAct < 59.9))
17456  // -1 indicates don't display
17457  {
17458  OpTimeToActMultiMapEntry.first = TimeToAct;
17459  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
17460  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
17461  }
17462  }
17463  }
17464 
17465 /*
17466  * class TContinuationTrainExpectationEntry
17467  {
17468  public:
17469  AnsiString Description; ///< service description
17470  AnsiString HeadCode; ///< service headcode
17471  int RepeatNumber; ///< service RepeatNumber
17472  int IncrementalMinutes; ///< Repeat separation in minutes
17473  int IncrementalDigits; ///< Repeat headcode separation
17474  int VectorPosition; ///< TrackVectorPosition for the continuation element
17475  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
17476  };
17477 
17478  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
17479  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
17480  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
17481  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
17482 */
17483 
17485  // build OpTimeToActMultiMap entries for expected trains
17486  {
17487  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
17488  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
17489  float TimeToAct = 0; // minutes
17490  int DistanceToRedSignal = 0; // metres
17491  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
17492  // used to ensure only one train displayed for a given continuation
17493  ContinuationEntryVecPosVector.clear();
17494  bool LaterTrain = false;
17497  {
17498  LaterTrain = false;
17499  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
17500  {
17501  CTEIt++;
17502  continue; // not interested in running or exited trains
17503  }
17504  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
17505  {
17506  CTEIt++;
17507  continue;
17508  // don't include trains not entered yet when a train is already on the continuation
17509  }
17510  if(!ContinuationEntryVecPosVector.empty())
17511  {
17512  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
17513  {
17514  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
17515  {
17516  LaterTrain = true; ;
17517  // skip past remaining trains waiting to enter at same point
17518  break;
17519  }
17520  }
17521  }
17522  if(LaterTrain)
17523  {
17524  CTEIt++;
17525  continue;
17526  }
17527  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
17528  AnsiString HeadCode = CTEIt->second.HeadCode;
17529  float CurrentStopTime; // set to 0 at start of function
17530  float LaterStopTime; // set to 0 at start of function
17531  float RecoverableTime; // set to 0 at start of function
17532  int AvTrackSpeed; // set to 0 at start of function
17533  int TrainID = -1; // not yet allocated for train still to enter
17534  bool SigControlAndCanPassRedSignal = false;
17535  // doesn't apply for a continuation
17536  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
17537  // EntryPos always 0 for entering at a continuation
17538  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(1),
17539  // at(1) to skip past the Start train value
17540  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed);
17541  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
17542  // for a train it's the one in front of LeadElement
17543  if(AvTrackSpeed < 30)
17544  AvTrackSpeed = 30;
17545  if(DistanceToRedSignal == -1)
17546  {
17547  TimeToAct = 60.0;
17548  }
17549  else
17550  {
17551  int Speed = AvTrackSpeed;
17552  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
17553  if(AvTrackSpeed > MaxSpeed)
17554  Speed = MaxSpeed;
17555  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(1).SignallerControl)
17556  // defined in timetable as under signaller control
17557  {
17558  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
17559  LaterStopTime = 0;
17560  }
17561  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
17562  // accel & decel taken into account in
17563  // CalcDistanceToRedSignalandStopTime
17564  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
17565  // don't need CurrentStopTime or RecoverableTime for continuation entries
17566  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
17567  TimeToAct += MinsBefEnter;
17568  }
17569  THCandTrainPosParam HCandTrainPosParam;
17570  HCandTrainPosParam.first = HeadCode;
17571  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
17572  // -1-CTE... because 2nd value covers TrainID if +ve &
17573  // continuation track vector position if -ve, -1 allows for vecpos being 0
17574  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
17575  {
17576  OpTimeToActMultiMapEntry.first = TimeToAct;
17577  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
17578  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
17579  }
17580  CTEIt++;
17581  }
17582  }
17583  Utilities->CallLogPop(2081);
17584 }
17585 
17586 // ---------------------------------------------------------------------------
17587 
17588 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
17589  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
17590  float &RecoverableTime, int &AvTrackSpeed)
17591  // new v2.2.0
17592  // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
17593  // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
17594  // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
17595  // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
17596  // aren't used - this means there is no display for the train in question
17597 {
17598  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
17599  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
17600  int DistanceToRedSignal = 0;
17601  int CumTrackSpeed = 0;
17602  // average track speed, in case need to use in time calc
17603  int TrackSpeedCount = 0;
17604 
17605  //below added at v2.6.1
17606  if(TrainID > -1)
17607  {
17608  TTrain &Train = TrainVectorAtIdent(51, TrainID);
17609  Train.DistanceToStationStop = 0;//if find a red signal first then this distance isn't needed
17610  Train.StationStopCalculated = false;
17611  }
17612 
17613  AvTrackSpeed = 0;
17614  int CurrentElement = TrackVectorPosition;
17615  int CurrentEntryPos = TrackVectorPositionEntryPos;
17616  int NextElement;
17617  int NextEntryPos;
17618  int NextExitPos;
17619 
17620  CurrentStopTime = 0;
17621  LaterStopTime = 0;
17622  RecoverableTime = 0;
17623  if(CurrentElement == -1) // end element, no action needed
17624  {
17625  Utilities->CallLogPop(2094);
17626  return -1;
17627  }
17628  int CurrentExitPos;
17629 
17630  // get ExitPos for first element to be measured
17631  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
17632  {
17633  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
17634  {
17635  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
17636  CurrentExitPos = 1;
17637  else
17638  CurrentExitPos = 3;
17639  }
17640  else
17641  CurrentExitPos = 0; // trailing point
17642  }
17643  else
17644  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
17645  // get CumTrackSpeed for first measured element
17646 
17647  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
17648  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
17649 
17650  // check if currently stopped at a location, and if so add the remaining dwell time
17651  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
17652  if(TrainID > -1)
17653  // -1 for a continuation and can't be at a location as not yet entered
17654  {
17655  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
17657  // this used to deduct from RecoverableTime when arrive at a location
17658  if(Train.StoppedAtLocation)
17659  {
17660  if(Train.StoppedForTrainInFront)
17661  {
17662  Utilities->CallLogPop(2082);
17663  return -1; // no action needed
17664  }
17665  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
17666  {
17667  Utilities->CallLogPop(2083);
17668  return -1; // not due a departure so no action needed
17669  }
17670  else // due a departure
17671  {
17672  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
17673  // can't convert a TDateTime to a float directly
17674  CurrentStopTime = float(TimeToDepart);
17675  AVPtr++;
17676  }
17677  }
17678  }
17679 
17680  // check if CurrentElement is a red signal, but ok if autosig route after
17681  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
17682  // ok if autosig route after red signal
17683  {
17684  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
17685  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
17686  int RouteNumber; // holder for referenced value, not used
17687  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
17688  {
17689  Utilities->CallLogPop(2078);
17690  return -1;
17691  }
17692  else if(SigControlAndCanPassRedSignal)
17693  // ignore signal and increment CurrentElement to NextElement
17694  {
17695  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
17696  {
17697  if((NextEntryPos == 0) || (NextEntryPos == 2))
17698  // leading entry point
17699  {
17700  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
17701  NextExitPos = 1;
17702  else
17703  NextExitPos = 3;
17704  }
17705  else
17706  NextExitPos = 0; // trailing entry point
17707  }
17708  else
17709  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
17710 
17711  CurrentElement = NextElement;
17712  CurrentEntryPos = NextEntryPos;
17713  CurrentExitPos = NextExitPos;
17714  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
17715  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
17716  }
17717  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
17718  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
17719  {
17720  Utilities->CallLogPop(2084);
17721  return 0;
17722  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
17723  }
17724  }
17725  int LaterStopNumber = 0;
17726  int x = 0;
17727  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
17728 
17729  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
17730  // not red signal next (in fwd direction) so enter loop to calc CumLength
17731  {
17732  x++; // added in v2.4.0 as above
17733  if(x > 5000)
17734  {
17735  Utilities->CallLogPop(2120);
17736  return -1;
17737  }
17738  if(CurrentEntryPos > 1)
17739  {
17740  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
17741  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
17742  }
17743  else
17744  {
17745  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
17746  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
17747  }
17748  TrackSpeedCount++;
17749 
17750  // added at v2.6.1 to find DistanceToStationStop for trains running early
17751  if(TrainID > -1) //can ignore continuation entries as these don't run early
17752  {
17753  TTrain &Train = TrainVectorAtIdent(52, TrainID);
17754  if(!Train.StationStopCalculated)
17755  {
17756  if(Train.TrainMode == Timetable)
17757  {
17758  bool StopRequired = false;
17759  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
17760  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
17761  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
17762  {
17763  // no need to add in the length of element to CumulativeLength
17764  if(StopRequired)
17765  {
17766  Train.DistanceToStationStop = DistanceToRedSignal;// DistanceToRedSignal holds the intermediate distance to this point
17767  Train.StationStopCalculated = true; //don't want to update it with later stops
17768  }
17769  }
17770  }
17771  }
17772  }
17773 
17774  // check for train in front, but if on a bridge on other track then ok
17775  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
17776  int TrainOnElement;
17777  if(TE.TrackType != Bridge)
17778  {
17779  TrainOnElement = TE.TrainIDOnElement;
17780  }
17781  else
17782  {
17783  if(CurrentEntryPos > 1)
17784  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
17785  else
17786  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
17787  }
17788  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
17789  // train in front before red signal
17790  {
17791  Utilities->CallLogPop(2085);
17792  return -1;
17793  }
17794 
17795  // add to stoptime if required
17796  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
17797  {
17798  double StopTimeDouble;
17799  while(AVPtr->FormatType == PassTime)
17800  AVPtr++; // skip past any passes
17801  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
17802  (AVPtr->FormatType == TimeTimeLoc)))
17803  // stop due here so calc dwell time & advance Ptr
17804  {
17805  if(AVPtr->FormatType == TimeTimeLoc)
17806  {
17807  LaterStopNumber++;
17808  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
17809  if(StopTimeDouble < 0.5)
17810  StopTimeDouble = 0.5;
17811  // at least 30 secs delay at station
17812  // can't convert a TDateTime to a float directly
17813  LaterStopTime += float(StopTimeDouble);
17814  RecoverableTime += StopTimeDouble - 0.5;
17815  if((LaterStopNumber == 1) && (TrainID > -1))
17816  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
17817  AVPtr++;
17818  }
17819  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
17820  {
17821  if((AVPtr + 1)->FormatType == TimeLoc)
17822  // must be a departure
17823  {
17824  LaterStopNumber++;
17825  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
17826  // can't convert a TDateTime to a float directly
17827  if(StopTimeDouble < 0.5)
17828  StopTimeDouble = 0.5;
17829  // at least 30 secs delay at station
17830  LaterStopTime += float(StopTimeDouble);
17831  RecoverableTime += StopTimeDouble - 0.5;
17832  if((LaterStopNumber == 1) && (TrainID > -1))
17833  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
17834  AVPtr++;
17835  AVPtr++;
17836  }
17837  else // not a departure, does something else at the location so no calculation needed
17838  {
17839  Utilities->CallLogPop(2086);
17840  return -1;
17841  }
17842  }
17843  }
17844  }
17845 
17846  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
17847  if(NextElement == -1) // reached end element, no action needed
17848  {
17849  Utilities->CallLogPop(2077);
17850  return -1;
17851  }
17852  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
17853  // get NextExitPos
17854  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
17855  {
17856  if((NextEntryPos == 0) || (NextEntryPos == 2))
17857  // leading entry point
17858  {
17859  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
17860  NextExitPos = 1;
17861  else
17862  NextExitPos = 3;
17863  }
17864  else
17865  NextExitPos = 0; // trailing entry point
17866  }
17867  else
17868  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
17869 
17870  CurrentElement = NextElement;
17871  CurrentEntryPos = NextEntryPos;
17872  CurrentExitPos = NextExitPos;
17873  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
17874  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
17875  }
17876  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
17877  // ok if autosig route after red signal, no action needed
17878  {
17879  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
17880  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
17881  int RouteNumber; // holder for referenced value, not used
17882  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
17883  {
17884  Utilities->CallLogPop(2095);
17885  return -1;
17886  }
17887  }
17888  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
17889  // average line speed/2 (for half distance accelerating and half decelerating.
17890 
17891  float MaxAllowableSpeed;
17892 
17893  if(TrackSpeedCount > 0)
17894  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
17895  else // shouldn't reach here but include to prevent divide by zero error
17896  {
17897  if(CurrentEntryPos > 1)
17898  {
17899  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
17900  }
17901  else
17902  {
17903  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
17904  }
17905  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
17906  }
17907  float KmPerLocationStop;
17908 
17909  if(LaterStopNumber > 0)
17910  {
17911  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
17912  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
17913  }
17914  else
17915  {
17916  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
17917  // using linear trendline for accel & decel distance at various speeds
17918  // at half braking, speed never < 60 using this
17919  }
17920  if(AvTrackSpeed > MaxAllowableSpeed)
17921  AvTrackSpeed = MaxAllowableSpeed;
17922  Utilities->CallLogPop(2096);
17923  return DistanceToRedSignal;
17924 }
17925 
17926 // ---------------------------------------------------------------------------
17927 // end of TTrainController entries
17928 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:7645
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:16162
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:106
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:339
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:49
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:13274
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:306
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:305
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:435
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:635
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:62
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1586
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:884
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:144
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:182
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:13297
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:12986
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:327
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:17432
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:743
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:14310
TTrain::CheckNewServiceDepartureTime
AnsiString CheckNewServiceDepartureTime(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time
Definition: TrainUnit.cpp:6543
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:90
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:50
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:980
Create
@ Create
Definition: TrainUnit.h:49
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:722
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:17171
Arrive
@ Arrive
Definition: TrainUnit.h:49
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:49
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:304
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:345
Depart
@ Depart
Definition: TrainUnit.h:49
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:702
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:10218
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:700
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:385
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:689
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:289
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:6553
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1504
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:50
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:890
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1516
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:625
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:112
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:673
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1527
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:64
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:62
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:452
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:683
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:5732
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:63
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:389
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:66
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:17891
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:12260
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:149
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:327
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:966
TTrain::TrainFailed
bool TrainFailed
added at v2.4.0 to indicate failure
Definition: TrainUnit.h:371
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:384
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:949
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:729
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:300
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:945
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:6779
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:347
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:426
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:327
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:5836
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:729
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:8662
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:307
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:881
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:657
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:14040
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:972
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:550
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:979
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:184
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:16959
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:729
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:10171
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1508
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:7617
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:106
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:932
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:679
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:14055
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:235
Intermediate
@ Intermediate
Definition: TrainUnit.h:71
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:930
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:5665
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:946
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3398
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:620
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:62
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:448
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:164
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:410
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:8624
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:51
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:441
NotStarted
@ NotStarted
Definition: TrainUnit.h:80
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:9783
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:776
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:725
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:307
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:705
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:335
TTrainDataEntry
Contains all data for a single train.
Definition: TrainUnit.h:177
LeadMid
@ LeadMid
Definition: TrainUnit.h:268
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:769
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:17411
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:13069
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:944
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:5857
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:675
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:986
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:222
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:982
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:185
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:786
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:749
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:665
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:7885
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:14159
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:268
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:221
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:822
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:15002
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:14097
StartNew
@ StartNew
Definition: TrainUnit.h:62
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:49
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:268
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:693
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:308
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:50
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:400
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:627
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:16710
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:9779
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:955
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5182
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:455
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:156
TTrain
Definition: TrainUnit.h:271
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:952
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:9798
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:151
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:10391
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:817
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:220
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:799
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:432
GapJump
@ GapJump
Definition: TrackUnit.h:63
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:973
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:771
NoSequence
@ NoSequence
Definition: TrainUnit.h:71
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:147
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:147
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:367
Finish
@ Finish
Definition: TrainUnit.h:71
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:5618
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:8263
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:327
TTrackElement::TempTrackMarker23
bool TempTrackMarker23
Utility markers for program use.
Definition: TrackUnit.h:137
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1554
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:327
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:321
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:195
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:638
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:325
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:14138
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:766
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:290
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:174
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:720
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:414
TExitListIterator
TExitList::iterator TExitListIterator
Definition: TrainUnit.h:88
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:653
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:13862
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1275
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:5413
End
@ End
Definition: TrackUnit.h:73
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:357
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6417
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:7958
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:302
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackElement.at(At)
Definition: TrackUnit.cpp:9524
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:63
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:295
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:363
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:14221
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:123
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:940
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:520
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:990
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:775
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:179
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:379
SignalPost
@ SignalPost
Definition: TrackUnit.h:63
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:934
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled action, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:418
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:143
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1531
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:310
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:962
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:179
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5015
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:10838
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:120
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:777
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:706
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:826
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:377
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:72
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:428
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:62
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:774
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:743
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:879
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1597
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:432
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:772
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:950
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:62
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:92
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:106
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:432
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TExitList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:9849
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:928
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:408
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:953
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:9148
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:677
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:327
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:406
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:74
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:978
Pass
@ Pass
Definition: TrainUnit.h:51
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:729
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:691
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:67
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:2882
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:10204
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:759
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:69
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:965
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:439
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:709
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:807
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:6832
TExitList
std::list< int > TExitList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:84
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:938
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:651
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:315
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:284
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:971
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:755
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:7629
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:974
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:181
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:62
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:191
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6282
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:746
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:15888
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:420
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:13551
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:9656
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:535
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:278
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3078
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:985
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:416
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:4690
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2427
RearSplit
@ RearSplit
Definition: TrainUnit.h:49
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:104
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:51
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:710
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:604
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:935
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:753
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:958
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:133
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:117
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:9559
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2814
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6254
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:13644
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:51
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:775
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:12365
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:164
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
new v2.2.0, incremented in Interface.cpp, controls updating for OpTimeToActPanel
Definition: TrainUnit.h:791
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:341
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:217
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3348
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:931
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:292
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:7438
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:6940
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2806
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:758
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:435
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:412
NewService
@ NewService
Definition: TrainUnit.h:49
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:14374
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1371
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:432
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:343
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1515
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:333
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:193
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:189
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:764
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:169
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:75
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:975
Crossover
@ Crossover
Definition: TrackUnit.h:63
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4333
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:671
AtLocation
@ AtLocation
Definition: TrainUnit.h:67
Signal
@ Signal
Definition: TrackUnit.h:73
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:937
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:968
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:682
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:151
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:16400
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:805
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:16552
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1516
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:414
Exited
@ Exited
Definition: TrainUnit.h:80
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackElement.at(At)
Definition: TrackUnit.cpp:9511
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:14071
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7172
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:16225
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:987
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2063
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:432
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:55
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1536
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:927
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:432
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:689
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:280
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:731
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:49
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:387
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:743
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:957
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:933
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:327
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:62
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:969
Leave
@ Leave
Definition: TrainUnit.h:49
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:779
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:701
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:727
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:404
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:355
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2787
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:936
NoFormat
@ NoFormat
Definition: TrainUnit.h:62
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:739
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:62
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2357
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:432
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3377
Enter
@ Enter
Definition: TrainUnit.h:49
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7037
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:943
TTrain::NewTrainService
void NewTrainService(int Caller)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:5798
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:14121
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:398
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:246
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:12904
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:681
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:7920
Timetable
@ Timetable
Definition: TrainUnit.h:56
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:123
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:13230
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6334
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:737
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:10058
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:673
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:102
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:16070
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:729
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:277
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:17332
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:447
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:75
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:393
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:699
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:233
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:735
Terminate
@ Terminate
Definition: TrainUnit.h:49
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:743
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
map of active track element names
Definition: TrackUnit.h:685
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:432
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:145
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:446
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:330
Running
@ Running
Definition: TrainUnit.h:80
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:255
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3468
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:880
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:12232
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:13655
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed)
new v2.2.0, calcs distance to red signal, returns -1 for no signal found, for autosigs route after ne...
Definition: TrainUnit.cpp:17588
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:14397
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:406
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:10279
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:623
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:215
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:780
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:381
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2260
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:17085
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:795
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1502
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:8349
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:7967
TTrainController::ControllerCheckNewServiceDepartureTime
AnsiString ControllerCheckNewServiceDepartureTime(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::CheckNewServiceDepartureTime for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:8847
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:251
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:960
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:126
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:7978
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:977
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1588
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:743
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3264
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:131
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:2977
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:414
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:693
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TExitList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:10259
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:67
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:51
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1500
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:7586
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:2850
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:94
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:14243
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:236
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:954
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:13628
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:143
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:797
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:433
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4289
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:248
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TExitList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:16911
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:8678
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:50
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:317
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:365
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:10342
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:95
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:7990
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:151
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:886
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:396
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:731
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:159
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1569
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:183
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:154
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:128
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:959
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:197
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:145
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:751
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:791
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:518
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:4713
TActionVectorEntry::ExitList
TExitList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:108
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:416
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1506
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1516
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:110
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:296
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:983
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:644
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6090
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:642
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:8333
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:878
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:765
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:313
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:14410
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:660
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:948
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:149
Points
@ Points
Definition: TrackUnit.h:63
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:724
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:353
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:760
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:12807
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6129
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:351
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:743
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:778
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:51
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3326
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:967
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:359
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:450
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:15377
Continuation
@ Continuation
Definition: TrackUnit.h:63
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:729
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:6805
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:658
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:773
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:424
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:9026
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:323
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:17018
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:2941
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:882
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:51
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:712
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:885
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:981
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:8709
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:85
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:430
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:614
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:637
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6033
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:437
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:271
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:179
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:294
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:457
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:51
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:964
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:17713
TTrackElement::TempTrackMarker01
bool TempTrackMarker01
Definition: TrackUnit.h:137
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:785
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:956
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:198
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:9922
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:282
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:259
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:618
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:653
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:7549
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:61
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:729
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:287
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:698
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:157
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:75
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:707
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:951
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:16861
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:16817
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:733
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:96
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:252
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:71
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:889
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:294
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:48
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:653
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:327
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:768
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:988
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:75
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:8303
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:13575
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:6592
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:16084
TTrain::AValue
double AValue
this is a useful shorthand value in calculating speeds and transit times in SetTrainMovementValues [=...
Definition: TrainUnit.h:373
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5067
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:16310
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:8318
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:118
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:12239
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:303
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:7693
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:14203
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:155
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:154
NoLocation
@ NoLocation
Definition: TrainUnit.h:67
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:899
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:349
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:187
NoMode
@ NoMode
Definition: TrainUnit.h:56
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:770
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:10688
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:98
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:702
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:743
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:62
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:146
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:210
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:788
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:763
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:741
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:457
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:4796
TTrainController::OperatingTrainArrDep
int OperatingTrainArrDep
< all these set to 0 in constructor
Definition: TrainUnit.h:782
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:432
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:8697
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:947
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:139
TTrainController::NotStartedTrainArrDep
int NotStartedTrainArrDep
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:784
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:267
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:714
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable (the object is a member of TTrainController)
Definition: TrainUnit.h:210
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:13262
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:6637
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:402
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:640
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1510
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:114
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:319
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:704
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:941
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:984
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:700
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:2908
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled action entries, null on creation
Definition: TrainUnit.h:96
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:116
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:369
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:976
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:96
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4445
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:673
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6189
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:147
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:361
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:141
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:743
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:939
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:5676
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:709
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:62
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:50
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:703
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:17286
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:111
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:669
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:62
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:757
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:970
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:801
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:655
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:98
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:4702
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:391
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:147
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:659
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:298
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:648
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:713
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:63
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:767
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2832
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:87
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:604
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1037
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:961
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:16593
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6061
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3301
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:100
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2440
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:189
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:179
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6365
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:301
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:73
Repeat
@ Repeat
Definition: TrainUnit.h:63
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:929
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:12248
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:586
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:743
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:156
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:64
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:149
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:433
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:963
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:642
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:812
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:218
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5343
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:16336
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:141
Signaller
@ Signaller
Definition: TrainUnit.h:56
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:443
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:887
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:153
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:876
Bridge
@ Bridge
Definition: TrackUnit.h:63
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:942
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:250
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:327
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:70
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:383
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:71
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:63
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:104
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:96